Compare commits

..
82 Commits
Author SHA1 Message Date
Sergey Lyubka eed09600f7 Ignoring .htpasswd file for PUT and DELETE requests. Those use separate passwords file. 2012-12-25 11:49:41 +00:00
Sergey Lyubka 8d6634ff7e Allowing HEAD method for CGI requests 2012-12-25 11:22:48 +00:00
Sergey Lyubka 6c9ed3ca53 Changed GetFullPathName -> GetFullPathNameA 2012-12-25 11:08:03 +00:00
Sergey Lyubka ede1befffa Fix read_websocket() for content length > 125 2012-12-25 11:05:19 +00:00
Sergey Lyubka 2cd4852321 Restore SIGCHLD handler to SIG_DFL in CGI process 2012-12-25 11:00:29 +00:00
Sergey Lyubka 1eb719f2d9 Checking for endianness in runtime in MD5 and SHA code 2012-12-24 21:23:34 +00:00
Sergey Lyubka bf633082ac Update README.md 2012-12-24 14:09:48 +00:00
Sergey Lyubka 075a152779 Update README.md 2012-12-24 11:09:44 +00:00
Sergey Lyubka c079a13b24 Update README.md 2012-12-24 11:09:23 +00:00
Sergey Lyubka 113c9d5a02 Fix keep-alive handling in process_new_connection() 2012-12-24 11:01:56 +00:00
Sergey Lyubka 05fa677382 Fixed unit test to accomodate mg_get_var() API change 2012-12-24 10:43:15 +00:00
Sergey Lyubka ab3c070e1c Merge pull request #66 from nullable-type/master
Made behavior of mg_get_cookie(..) regarding errors the same as of mg_get_var(..)
2012-12-23 04:40:35 -08:00
Sergey Lyubka 2e6a01b931 Fix for the relative document root & PHP 2012-12-22 18:24:48 +00:00
Sergey Lyubka 9fa66bf237 Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-12-22 08:55:36 +00:00
Sergey Lyubka 98c382e7af Merge pull request #67 from tayS/ssl_pending
Fix for hang when sending large request with SSL enabled
2012-12-22 00:55:07 -08:00
Sergey Lyubka 94c7a3862a Update README.md 2012-12-21 21:47:14 +00:00
tayS 6869b64b31 Fix for hang when sending large request with SSL enabled
When using SSL, we must use SSL_pending() in addition to/instead of select() to determine whether there is SSL data to be read.
This update makes the SSL_pending() function available and uses is to check for data if select() indicates there is no data.

See https://github.com/valenok/mongoose/issues/47
2012-12-21 12:38:34 -06:00
nullable.type 8faf6f7b72 Made behavior of mg_get_cookie(..) on errors the same as mg_get_var(..). Separated the two cases a.) problems with destination buffer and b.) cookie header or cookie parameter not existing. 2012-12-16 22:20:21 +01:00
Sergey Lyubka 99ddb2f12a Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-12-16 13:09:08 +00:00
Sergey Lyubka ca9212c3ca Merge pull request #65 from nullable-type/master
Inconsistent error codes of mg_get_var(..)
2012-12-16 04:15:52 -08:00
nullable.type c9183f3851 Made the names of the function arguments of the declaration and the definition of mg_get_var(..) the same.
Changed the error codes to have a consistent way of reporting errors. -2 is returned if there is a problem with the size of the destination buffer. -1 is returned if the variable is not found.

Updated the documentation of mg_get_var(..) in mongoose.h and removed the redundant comments from mongoose.c.
2012-12-16 12:27:53 +01:00
Sergey Lyubka 0912a7f473 Merge pull request #60 from bakanatorz/master
Added support for Ogg Vorbis audio files
2012-12-09 13:26:57 -08:00
Sergey Lyubka 38421657b3 Minor refactoring in examples/upload.c 2012-12-08 14:53:46 +00:00
Sergey Lyubka 64cfe1d389 Merge pull request #49 from nullable-type/master
Avoid some level-4 warnings issued by Visual Studio (2012 and possibly previous versions)
2012-12-08 06:47:35 -08:00
Sergey Lyubka 69b0b58d46 Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-12-07 01:50:52 +00:00
Sergey Lyubka 0d442058e5 Added mg_upload() 2012-12-07 01:50:47 +00:00
Sergey Lyubka 1ef31461bc Added test_skip_quoted() 2012-12-07 01:50:12 +00:00
Sergey Lyubka 0b09df97ac On new connection, clear off receiving buffer 2012-12-05 23:07:20 +00:00
Sergey Lyubka 0263e39584 Minor refactoring 2012-11-30 15:01:15 +00:00
Sergey Lyubka d8424726bb Using O_BINARY on win32 for uploaded files 2012-11-30 10:09:39 +00:00
Sergey Lyubka 080cb2dd4b Changed signature of url_decode(): signalling on destination buffer overflow. mg_get_var() now does not require extra space for variable buffer 2012-11-14 09:26:02 -08:00
Eric Bakan 786caaecb2 Added support for Ogg Vorbis audio files 2012-11-12 10:40:19 -05:00
nullable.type eb92b5f1b3 Avoid memory leaks if duplicate options are passed. 2012-11-02 22:52:47 +01:00
nullable.type c5ba6016aa Suppress warning C4127 (conditional expression is constant) issued by Visual Studio 2012 introduced by FD_SET(..) which is using the do-while(0) trick. 2012-10-27 14:09:26 +02:00
nullable.type 73a845a327 Suppress warning C4204 (non-constant aggregate initializer) issued by Visual Studio 2012 due to missing C99 support. 2012-10-27 14:07:06 +02:00
nullable.type 6eec552ea4 Added declaration to avoid warning C4115 (named type definition in parentheses) issued by Visual Studio 2012. 2012-10-27 14:04:41 +02:00
Sergey Lyubka cb62203cf3 More documentation for MG_OPEN_FILE event 2012-10-25 21:47:04 +01:00
Sergey Lyubka 4fcb64fe90 Fix exception in win32 code path when CGI exec fails 2012-10-24 10:12:32 +01:00
Sergey Lyubka 8966f47c7a Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-10-23 20:21:38 +01:00
Sergey Lyubka e251375c0c Using .lp extension for Lua server pages 2012-10-23 20:21:24 +01:00
Sergey Lyubka 672e1ec8c6 Merge pull request #44 from mitchh/master
Fix 30s hang->timeout when closing a socket with no more data
2012-10-20 00:39:51 -07:00
Mitch Hendrickson f8713ede2d Use existing must_close flag instead of adding is_closing 2012-10-19 19:51:01 -07:00
Mitch Hendrickson 0b5f854bf0 Fix 30s timeout when closing a socket that has no remaining data, introduced in 375950f6; setting the socket nonblocking makes recv() return instantly but not select() 2012-10-15 23:09:14 -07:00
Sergey Lyubka 3770020b74 Remove winsock include - windows.h does that for us 2012-10-14 15:08:43 +01:00
Sergey Lyubka 9f9a1638f7 clean target added 2012-10-14 15:00:04 +01:00
Sergey Lyubka fb07a937e7 Merge pull request #43 from sebbern/master
Winsock2.h included after windows.h
2012-10-13 04:35:06 -07:00
Sebastian Reinhard 025cd283c4 Fixed an error in mongoose.c that caused a lot of errors where winsock2.h was included after windows.h 2012-10-13 12:47:51 +02:00
Sergey Lyubka b39426dbf7 Showing error message 2012-10-12 14:28:35 +01:00
Sergey Lyubka 5157bd0960 Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-10-10 15:12:30 +01:00
Sergey Lyubka 2aaedef4af Trimming whitespaces properly in windows CGI interpreter name 2012-10-10 15:12:16 +01:00
Sergey Lyubka a0b037d288 Merge pull request #39 from shantanugadgil/patch-1
Add mime type for .xsl
2012-10-08 00:53:50 -07:00
shantanugadgil 011bdc22b2 Update mongoose.c
Add default MIME type for xsl
2012-10-08 09:55:33 +05:30
Sergey Lyubka b437d56736 Added MG_INIT_LUA event 2012-10-08 00:09:43 +01:00
Sergey Lyubka 6205e69ab2 Fixed python bindings 2012-10-07 16:26:04 +01:00
Sergey Lyubka 038f8823f1 Added test to mg_stat(). Fixed set_gpass() 2012-10-05 08:58:13 +01:00
Sergey Lyubka 03dd8367bf Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-10-05 02:10:47 +01:00
Sergey Lyubka b597da34da Pass user_data to the callback 2012-10-05 02:10:35 +01:00
Sergey Lyubka bf387f9544 Added Lua build instructions 2012-10-01 22:24:22 +01:00
Sergey Lyubka 365b5e8f8a Using long for file size in MG_OPEN_FILE ev_data 2012-10-01 10:19:20 +01:00
Sergey Lyubka 47651766fb Squashed warning under linux 2012-09-30 16:35:09 +01:00
Sergey Lyubka 043ea54a2a Removed test cruft for MG_OPEN_FILE message from the event handler 2012-09-29 22:24:16 +01:00
Sergey Lyubka 55c8c82c4b Fixed windows build 2012-09-29 22:21:10 +01:00
Sergey Lyubka 557d1f4c49 Added MG_OPEN_FILE message 2012-09-29 18:56:41 +01:00
Sergey Lyubka 2539a473fe Added test for byte range past EOF 2012-09-29 18:56:23 +01:00
Sergey Lyubka 6bd35a4c18 Style changes 2012-09-28 10:36:31 +01:00
Sergey Lyubka fb8f0ed4d6 Better comments in Makefile 2012-09-27 23:58:35 +01:00
Sergey Lyubka 3ffdf5459a API change: folded user_data into request_info, and introduced event-specific ev_data 2012-09-27 22:20:02 +01:00
Sergey Lyubka 02098b1903 API change: folded user_data into request_info, and introduced event-specific ev_data 2012-09-27 21:48:05 +01:00
Sergey Lyubka c189553c8b Cosmetic changes for CYA_SOURCES 2012-09-27 08:27:45 +01:00
Sergey Lyubka d970705596 #define MAP_PRIVATE 0 for _WIN32 2012-09-27 08:19:14 +01:00
Sergey Lyubka 5c9ab40601 Using MAP_PRIVATE for mmap, to fix mac build for Lua 2012-09-27 08:17:31 +01:00
Sergey Lyubka 451d077170 cygwin target added 2012-09-27 00:33:33 +01:00
Sergey Lyubka 30d1573346 Fixed spawn_proccess() for win32 for .exe binaries 2012-09-27 00:27:20 +01:00
Sergey Lyubka 0adef2db5b Merge branch 'master' of ssh://github.com/valenok/mongoose 2012-09-26 23:37:15 +01:00
Sergey Lyubka 8c1866a4af Lua server pages support 2012-09-26 23:37:03 +01:00
Sergey Lyubka 2a7a594b49 Squashed warning under linux 2012-09-25 12:35:30 +01:00
Sergey Lyubka 78ea04556f Windows build for examples added 2012-09-24 21:25:06 +01:00
Sergey Lyubka 7fe2944477 Squashed warnings in win32 websocket code 2012-09-24 21:24:02 +01:00
Sergey Lyubka 93131417e6 is_websocket_request() modified to check for the presence of keyword rather then euqality 2012-09-24 09:38:02 +01:00
Sergey Lyubka 6ef5eb0665 Changed overview 2012-09-24 09:04:28 +01:00
Sergey Lyubka 24103ff5df Added more things to features section 2012-09-23 15:06:09 +01:00
Sergey Lyubka 2118de37c3 3.3 -> 3.4 2012-09-23 15:02:18 +01:00
14 changed files with 1191 additions and 614 deletions

No files matched your search

+56 -40
View File
@@ -4,7 +4,7 @@
PROG= mongoose PROG= mongoose
all: all:
@echo "make (linux|bsd|solaris|mac|windows|mingw)" @echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386) # Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386)
# -DHAVE_MD5 - use system md5 library (-2kb) # -DHAVE_MD5 - use system md5 library (-2kb)
@@ -12,18 +12,23 @@ all:
# -DDEBUG - build debug version (very noisy) (+7kb) # -DDEBUG - build debug version (very noisy) (+7kb)
# -DNO_CGI - disable CGI support (-5kb) # -DNO_CGI - disable CGI support (-5kb)
# -DNO_SSL - disable SSL functionality (-2kb) # -DNO_SSL - disable SSL functionality (-2kb)
# -DNO_SSL_DL - link against system libssl library (-1kb)
# -DCONFIG_FILE=\"file\" - use `file' as the default config file # -DCONFIG_FILE=\"file\" - use `file' as the default config file
# -DHAVE_STRTOUI64 - use system strtoui64() function for strtoull()
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object # -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so # -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
########################################################################## ##########################################################################
### UNIX build: linux, bsd, mac, rtems ### UNIX build: linux, bsd, mac, rtems
########################################################################## ##########################################################################
# 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
LUA = lua-5.2.1/src
LUA_FLAGS = -DUSE_LUA -I$(LUA) -L$(LUA) -llua -lm
GCC_WARNS = -W -Wall -pedantic 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) 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)
@@ -64,50 +69,51 @@ CYA = e:/cyassl-2.0.0rc2
#DBG = /Zi /DDEBUG /Od #DBG = /Zi /DDEBUG /Od
DBG = /DNDEBUG /O1 DBG = /DNDEBUG /O1
CL = $(MSVC)/bin/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 /I$(MSVC)/include /DUSE_LUA /I$(LUA)
GUILIB= user32.lib shell32.lib GUILIB= user32.lib shell32.lib
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \ LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib /subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \ 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= \ LUA_SOURCES = $(LUA)/lapi.c $(LUA)/lcode.c $(LUA)/lctype.c \
$(CYA)/src/cyassl_int.c \ $(LUA)/ldebug.c $(LUA)/ldo.c $(LUA)/ldump.c \
$(CYA)/src/cyassl_io.c \ $(LUA)/lfunc.c $(LUA)/lgc.c $(LUA)/llex.c \
$(CYA)/src/keys.c \ $(LUA)/lmem.c $(LUA)/lobject.c $(LUA)/lopcodes.c \
$(CYA)/src/tls.c \ $(LUA)/lparser.c $(LUA)/lstate.c $(LUA)/lstring.c \
$(CYA)/src/ssl.c \ $(LUA)/ltable.c $(LUA)/ltm.c $(LUA)/lundump.c \
$(CYA)/ctaocrypt/src/aes.c \ $(LUA)/lvm.c $(LUA)/lzio.c $(LUA)/lauxlib.c \
$(CYA)/ctaocrypt/src/arc4.c \ $(LUA)/lbaselib.c $(LUA)/lbitlib.c $(LUA)/lcorolib.c \
$(CYA)/ctaocrypt/src/asn.c \ $(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
$(CYA)/ctaocrypt/src/coding.c \ $(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
$(CYA)/ctaocrypt/src/ctc_asm.c \ $(LUA)/loadlib.c $(LUA)/linit.c
$(CYA)/ctaocrypt/src/ctc_misc.c \ LUA_OBJECTS = $(LUA_SOURCES:%.c=%.o)
$(CYA)/ctaocrypt/src/cyassl_memory.c \
$(CYA)/ctaocrypt/src/des3.c \ CYA_SOURCES = $(CYA)/src/cyassl_int.c $(CYA)/src/cyassl_io.c \
$(CYA)/ctaocrypt/src/dh.c \ $(CYA)/src/keys.c $(CYA)/src/tls.c $(CYA)/src/ssl.c \
$(CYA)/ctaocrypt/src/dsa.c \ $(CYA)/ctaocrypt/src/aes.c $(CYA)/ctaocrypt/src/arc4.c \
$(CYA)/ctaocrypt/src/ecc.c \ $(CYA)/ctaocrypt/src/asn.c $(CYA)/ctaocrypt/src/coding.c \
$(CYA)/ctaocrypt/src/hc128.c \ $(CYA)/ctaocrypt/src/ctc_asm.c $(CYA)/ctaocrypt/src/ctc_misc.c \
$(CYA)/ctaocrypt/src/hmac.c \ $(CYA)/ctaocrypt/src/cyassl_memory.c $(CYA)/ctaocrypt/src/des3.c \
$(CYA)/ctaocrypt/src/integer.c \ $(CYA)/ctaocrypt/src/dh.c $(CYA)/ctaocrypt/src/dsa.c \
$(CYA)/ctaocrypt/src/md4.c \ $(CYA)/ctaocrypt/src/ecc.c $(CYA)/ctaocrypt/src/hc128.c \
$(CYA)/ctaocrypt/src/md5.c \ $(CYA)/ctaocrypt/src/hmac.c $(CYA)/ctaocrypt/src/integer.c \
$(CYA)/ctaocrypt/src/pwdbased.c \ $(CYA)/ctaocrypt/src/md4.c $(CYA)/ctaocrypt/src/md5.c \
$(CYA)/ctaocrypt/src/rabbit.c \ $(CYA)/ctaocrypt/src/pwdbased.c $(CYA)/ctaocrypt/src/rabbit.c \
$(CYA)/ctaocrypt/src/random.c \ $(CYA)/ctaocrypt/src/random.c $(CYA)/ctaocrypt/src/ripemd.c \
$(CYA)/ctaocrypt/src/ripemd.c \ $(CYA)/ctaocrypt/src/rsa.c $(CYA)/ctaocrypt/src/sha.c \
$(CYA)/ctaocrypt/src/rsa.c \ $(CYA)/ctaocrypt/src/sha256.c $(CYA)/ctaocrypt/src/sha512.c \
$(CYA)/ctaocrypt/src/sha.c \ $(CYA)/ctaocrypt/src/tfm.c
$(CYA)/ctaocrypt/src/sha256.c \
$(CYA)/ctaocrypt/src/sha512.c \
$(CYA)/ctaocrypt/src/tfm.c
cyassl.lib: cyassl.lib:
$(CL) $(CYASRC) $(CYAFL) $(DEF) $(CL) /Fo$(CYA)/ $(CYA_SOURCES) $(CYAFL) $(DEF)
$(MSVC)/bin/lib *.obj /out:$@ $(MSVC)/bin/lib $(CYA)/*.obj /out:$@
windows: cyassl.lib lua.lib:
$(CL) /c /Fo$(LUA)/ $(LUA_SOURCES)
$(MSVC)/bin/lib $(LUA_SOURCES:%.c=%.obj) /out:$@
windows: cyassl.lib lua.lib
$(MSVC)/bin/rc win32\res.rc $(MSVC)/bin/rc win32\res.rc
$(CL) /I win32 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
@@ -125,6 +131,16 @@ mingw:
$(CC) $(MINGWOPT) -Iwin32 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
# Build for Windows under Cygwin
#CYGWINDBG= -DDEBUG -O0 -ggdb
CYGWINDBG= -DNDEBUG -Os
CYGWINOPT= -W -Wall -mthreads -Wl,--subsystem,console $(CYGWINDBG) -DHAVE_STDINT $(GCC_WARNINGS) $(COPT)
cygwin:
windres ./win32/res.rc ./win32/res.o
$(CC) $(CYGWINOPT) mongoose.c -lws2_32 \
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
$(CC) $(CYGWINOPT) -Iwin32 mongoose.c main.c ./win32/res.o -lws2_32 \
-ladvapi32 -o $(PROG).exe
########################################################################## ##########################################################################
### Manuals, cleanup, test, release ### Manuals, cleanup, test, release
@@ -144,4 +160,4 @@ 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 $(PROG).exe *.dll *.lib rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib win32/res.o
+9 -5
View File
@@ -3,7 +3,7 @@ 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 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. 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, that can be done via command line flags or configuration file, which should be created in the same directory where mongoose itself lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
Features Features
@@ -11,12 +11,15 @@ Features
- Crossplatform - works on Windows, MacOS and most flavors of UNIX - Crossplatform - works on Windows, MacOS and most flavors of UNIX
- CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support - CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support
- Resumed download, URL rewrite support - Lua server pages (like PHP, but using Lua as a language instead of PHP)
- IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods - Resumed download, URL rewrite, IP-based ACL, Windows service
- Small footprint: executable size is 40 kB on Linux 2.6 i386 system - Excluding files from serving by URI pattern (file blacklist)
- Download speed limit based on client subnet or URI pattern
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
- 130 kilobytes Windows executable with all of the above compiled in, and no dependencies
- 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. - 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) - 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 - Super-liberal MIT license, great for commercial users!
Mailing list Mailing list
@@ -29,3 +32,4 @@ 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! I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
Many thanks to all who already did so: T.Barmann, D.Hughes, J.C.Sloan and 4 others. Appreciated, guys, you keep my brains going!
+9 -9
View File
@@ -11,24 +11,24 @@ import sys
def EventHandler(event, conn): def EventHandler(event, conn):
info = conn.info info = conn.info
if event == mongoose.HTTP_ERROR: if event == mongoose.HTTP_ERROR:
conn.printf('%s', 'HTTP/1.0 200 OK\r\n') conn.write('HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') conn.write('Content-Type: text/plain\r\n\r\n')
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.write('HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') conn.write('Content-Type: text/plain\r\n\r\n')
conn.printf('%s %s\n', info.request_method, info.uri) conn.write('[%s] [%s] [%s]\n' % (info.request_method, info.uri,
info.query_string))
if info.request_method == 'POST': if info.request_method == 'POST':
content_len = conn.get_header('Content-Length') content_len = conn.get_header('Content-Length')
post_data = conn.read(int(content_len)) post_data = conn.read(int(content_len))
my_var = conn.get_var(post_data, 'my_var') my_var = conn.get_var(post_data, 'my_var')
else: else:
my_var = conn.get_var(info.query_string, 'my_var') my_var = conn.get_var(info.query_string, 'my_var')
conn.printf('my_var: %s\n', my_var or '<not set>') conn.write('my_var: %s\n' % (my_var or '<not set>'))
conn.printf('HEADERS: \n') conn.write('HEADERS: \n')
for header in info.http_headers[:info.num_headers]: for header in info.http_headers[:info.num_headers]:
conn.printf(' %s: %s\n', header.name, header.value) conn.write(' %s: %s\n' % (header.name, header.value))
return True return True
elif event == mongoose.NEW_REQUEST and info.uri == '/form': elif event == mongoose.NEW_REQUEST and info.uri == '/form':
conn.write('HTTP/1.0 200 OK\r\n' conn.write('HTTP/1.0 200 OK\r\n'
+4 -8
View File
@@ -49,6 +49,8 @@ WEBSOCKET_CONNECT = 5
WEBSOCKET_READY = 6 WEBSOCKET_READY = 6
WEBSOCKET_MESSAGE = 7 WEBSOCKET_MESSAGE = 7
WEBSOCKET_CLOSE = 8 WEBSOCKET_CLOSE = 8
OPEN_FILE = 9
INIT_LUA = 10
class mg_header(ctypes.Structure): class mg_header(ctypes.Structure):
@@ -72,6 +74,8 @@ class mg_request_info(ctypes.Structure):
('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),
('user_data', ctypes.c_void_p),
('ev_data', ctypes.c_void_p),
] ]
@@ -86,10 +90,6 @@ 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)
@@ -137,10 +137,6 @@ 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.
+18
View File
@@ -8,3 +8,21 @@ all:
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post; $(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket; $(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
MSVC = e:/vc6
CL = $(MSVC)/bin/cl
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf \
/link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
windows:
$(CL) upload.c ../mongoose.c $(CLFLAGS)
$(CL) hello.c ../mongoose.c $(CLFLAGS)
$(CL) upload.c ../mongoose.c $(CLFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS)
$(CL) post.c ../mongoose.c $(CLFLAGS)
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
clean:
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
+3
View File
@@ -346,6 +346,9 @@ static void *event_handler(enum mg_event event,
// try to serve the request. // try to serve the request.
processed = NULL; processed = NULL;
} }
} else if (event == MG_EVENT_LOG) {
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
processed = NULL;
} else { } else {
processed = NULL; processed = NULL;
} }
+25 -96
View File
@@ -5,107 +5,33 @@
#include <string.h> #include <string.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#define strtoll strtol
typedef __int64 int64_t;
#else
#include <inttypes.h> #include <inttypes.h>
#include <unistd.h> #include <unistd.h>
#endif // !_WIN32
#include "mongoose.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) { 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 (event == MG_NEW_REQUEST) {
if (!strcmp(ri->uri, "/handle_post_request")) { if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
handle_file_upload(conn); mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
mg_upload(conn, "/tmp");
} else { } else {
// Show HTML form. // 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" mg_printf(conn, "HTTP/1.0 200 OK\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: text/html\r\n\r\n%s", "Content-Type: text/html\r\n\r\n%s",
@@ -113,9 +39,11 @@ static void *callback(enum mg_event event, struct mg_connection *conn) {
} }
// Mark as processed // Mark as processed
return ""; return "";
} else { } else if (event == MG_UPLOAD) {
return NULL; mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
} }
return NULL;
} }
int main(void) { int main(void) {
@@ -124,6 +52,7 @@ int main(void) {
ctx = mg_start(&callback, NULL, options); ctx = mg_start(&callback, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
pause();
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
+1 -1
View File
@@ -221,7 +221,7 @@ 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_log_message(conn)); printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
} }
// Returning NULL marks request as not handled, signalling mongoose to // Returning NULL marks request as not handled, signalling mongoose to
+761 -394
View File
File diff suppressed because it is too large. Load diff
+109 -39
View File
@@ -34,35 +34,104 @@ 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 {
char *request_method; // "GET", "POST", etc const char *request_method; // "GET", "POST", etc
char *uri; // URL-decoded URI const char *uri; // URL-decoded URI
char *http_version; // E.g. "1.0", "1.1" const char *http_version; // E.g. "1.0", "1.1"
char *query_string; // URL part after '?' (not including '?') or NULL const char *query_string; // URL part after '?', not including '?', or NULL
char *remote_user; // Authenticated user, or NULL if no auth used const char *remote_user; // Authenticated user, or NULL if no auth used
long remote_ip; // Client's IP address long remote_ip; // Client's IP address
int remote_port; // Client's port int remote_port; // Client's port
int is_ssl; // 1 if SSL-ed, 0 if not int is_ssl; // 1 if SSL-ed, 0 if not
int num_headers; // Number of headers int num_headers; // Number of headers
struct mg_header { struct mg_header {
char *name; // HTTP header name const char *name; // HTTP header name
char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[64]; // Maximum 64 headers
void *user_data; // User data pointer passed to the mg_start()
void *ev_data; // Event-specific data pointer
}; };
// Various events on which user-defined function is called by Mongoose. // Various events on which user-defined callback function is called by Mongoose.
enum mg_event { enum mg_event {
MG_NEW_REQUEST, // New HTTP request has arrived from the client // New HTTP request has arrived from the client.
MG_REQUEST_COMPLETE, // Mongoose has finished handling the request // If callback returns non-NULL, Mongoose stops handling current request.
MG_HTTP_ERROR, // HTTP error must be returned to the client // ev_data contains NULL.
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message MG_NEW_REQUEST,
MG_INIT_SSL, // SSL initialization, sent before certificate setup
MG_WEBSOCKET_CONNECT, // Sent on HTTP connect, before websocket handshake. // Mongoose has finished handling the request.
// If user callback returns NULL, then mongoose proceeds // Callback return value is ignored.
// with handshake, otherwise it closes the connection. // ev_data contains NULL.
MG_WEBSOCKET_READY, // Handshake has been successfully completed. MG_REQUEST_COMPLETE,
MG_WEBSOCKET_MESSAGE, // Incoming message from the client
MG_WEBSOCKET_CLOSE, // Client has closed the connection // 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,
}; };
@@ -155,12 +224,7 @@ int mg_modify_passwords_file(const char *passwords_file_name,
// Return information associated with the request. // Return information associated with the request.
// These functions always succeed. 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.
@@ -222,19 +286,19 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
// or request_info.query_string. // or request_info.query_string.
// data_len: length of the encoded data. // data_len: length of the encoded data.
// var_name: variable name to decode from the buffer // var_name: variable name to decode from the buffer
// buf: destination buffer for the decoded variable // dst: destination buffer for the decoded variable
// buf_len: length of the destination buffer // dst_len: length of the destination buffer
// //
// Return: // Return:
// On success, length of the decoded variable. // On success, length of the decoded variable.
// On error: // On error:
// -1 (variable not found, or destination buffer is too small). // -1 (variable not found).
// -2 (destination buffer is NULL or zero length). // -2 (destination buffer is NULL, zero length or too small to hold the decoded variable).
// //
// Destination buffer is guaranteed to be '\0' - terminated if it is not // Destination buffer is guaranteed to be '\0' - terminated if it is not
// NULL or zero length. In case of failure, dst[0] == '\0'. // NULL or zero length.
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 *dst, size_t dst_len);
// Fetch value of certain cookie variable into the destination buffer. // Fetch value of certain cookie variable into the destination buffer.
// //
@@ -244,9 +308,9 @@ int mg_get_var(const char *data, size_t data_len,
// //
// Return: // Return:
// On success, value length. // On success, value length.
// On error, -1 (either "Cookie:" header is not present at all, or the // On error:
// requested parameter is not found, or destination buffer is too small // -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
// to hold the value). // -2 (destination buffer is NULL, zero length or too small to hold the value).
int mg_get_cookie(const struct mg_connection *, int mg_get_cookie(const struct mg_connection *,
const char *cookie_name, char *buf, size_t buf_len); const char *cookie_name, char *buf, size_t buf_len);
@@ -277,6 +341,12 @@ 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);
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
int mg_upload(struct mg_connection *conn, const char *destination_dir);
// Convenience function -- create detached thread. // Convenience function -- create detached thread.
// Return: 0 on success, non-0 on error. // Return: 0 on success, non-0 on error.
typedef void * (*mg_thread_func_t)(void *); typedef void * (*mg_thread_func_t)(void *);
+3 -3
View File
@@ -119,10 +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; int status = (int) ri->ev_data;
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", mg_get_reply_status_code(conn)); "Conntection: close\r\n\r\n", status);
mg_printf(conn, "Error: [%d]", mg_get_reply_status_code(conn)); mg_printf(conn, "Error: [%d]", status);
} }
static void test_post(struct mg_connection *conn, static void test_post(struct mg_connection *conn,
+40
View File
@@ -0,0 +1,40 @@
<html>
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
<?
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
for i = 1, 100 do
if is_prime(i) then print('<span>' .. i .. '</span>&nbsp;') end
end
?>
<p>Reading POST data from Lua (click submit):</p>
<form method="POST"><input type="text" name="t1"/><input type="submit"></form>
<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>
</html>
+3
View File
@@ -180,6 +180,9 @@ my $cmd = "$exe ".
$cmd .= ' -cgi_interpreter perl' if on_windows(); $cmd .= ' -cgi_interpreter perl' if on_windows();
spawn($cmd); spawn($cmd);
o("GET /hello.txt HTTP/1.1\nConnection: close\nRange: bytes=3-50\r\n\r\n",
'Content-Length: 15\s', 'Range past the file end');
o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n", o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n",
'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close', 'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
'Request pipelining', 2); 'Request pipelining', 2);
+150 -19
View File
@@ -7,6 +7,8 @@
} while (0) } while (0)
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0) #define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
#define UNUSED_PORT "33796"
static void test_parse_http_request() { static void test_parse_http_request() {
struct mg_request_info ri; struct mg_request_info ri;
char req1[] = "GET / HTTP/1.1\r\n\r\n"; char req1[] = "GET / HTTP/1.1\r\n\r\n";
@@ -31,7 +33,7 @@ static void test_parse_http_request() {
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0); ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
ASSERT(strcmp(ri.http_headers[2].value, "") == 0); ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
// TODO(lsm): add more tests. // TODO(lsm): add more tests.
} }
static void test_should_keep_alive(void) { static void test_should_keep_alive(void) {
@@ -126,52 +128,61 @@ static void test_remove_double_dots() {
} }
static const char *fetch_data = "hello world!\n"; static const char *fetch_data = "hello world!\n";
static void *event_handler(enum mg_event event, static const char *inmemory_file_data = "hi there";
struct mg_connection *conn) { static void *event_handler(enum mg_event event, struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn); const struct mg_request_info *request_info = mg_get_request_info(conn);
if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) { if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n" mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: text/plain\r\n\r\n" "Content-Type: text/plain\r\n\r\n"
"%s", (int) strlen(fetch_data), fetch_data); "%s", (int) strlen(fetch_data), fetch_data);
return ""; 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) { } else if (event == MG_EVENT_LOG) {
printf("%s\n", mg_get_log_message(conn)); printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
} }
return NULL; return NULL;
} }
static void test_mg_fetch(void) { static void test_mg_fetch(void) {
static const char *options[] = { static const char *options[] = {
"document_root", ".", "document_root", ".",
"listening_ports", "33796", "listening_ports", UNUSED_PORT,
NULL, NULL,
}; };
char buf[2000], buf2[2000]; char buf[2000], buf2[2000];
int length; int n, length;
struct mg_context *ctx; struct mg_context *ctx;
struct mg_request_info ri; struct mg_request_info ri;
const char *tmp_file = "temporary_file_name_for_unit_test.txt"; const char *tmp_file = "temporary_file_name_for_unit_test.txt";
struct mgstat st; struct file file;
FILE *fp; FILE *fp;
ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL); ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);
// Failed fetch, pass invalid URL // Failed fetch, pass invalid URL
ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL); ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
ASSERT(mg_fetch(ctx, "localhost:33796", tmp_file, ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file,
buf, sizeof(buf), &ri) == NULL); buf, sizeof(buf), &ri) == NULL);
ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file, ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
buf, sizeof(buf), &ri) == NULL); buf, sizeof(buf), &ri) == NULL);
// Failed fetch, pass invalid file name // Failed fetch, pass invalid file name
ASSERT(mg_fetch(ctx, "http://localhost:33796/data", ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
"/this/file/must/not/exist/ever", "/this/file/must/not/exist/ever",
buf, sizeof(buf), &ri) == NULL); buf, sizeof(buf), &ri) == NULL);
// Successful fetch // Successful fetch
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/data", ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
tmp_file, buf, sizeof(buf), &ri)) != NULL); tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(ri.num_headers == 2); ASSERT(ri.num_headers == 2);
ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
@@ -184,12 +195,31 @@ static void test_mg_fetch(void) {
fclose(fp); fclose(fp);
// Fetch big file, mongoose.c // Fetch big file, mongoose.c
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/mongoose.c", ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c",
tmp_file, buf, sizeof(buf), &ri)) != NULL); tmp_file, buf, sizeof(buf), &ri)) != NULL);
ASSERT(mg_stat("mongoose.c", &st) == 0); ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
ASSERT(st.size == ftell(fp)); ASSERT(file.size == ftell(fp));
ASSERT(!strcmp(ri.request_method, "HTTP/1.1")); ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
// 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 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);
remove(tmp_file); remove(tmp_file);
mg_stop(ctx); mg_stop(ctx);
} }
@@ -208,23 +238,29 @@ static void test_base64_encode(void) {
} }
static void test_mg_get_var(void) { static void test_mg_get_var(void) {
static const char *post[] = {"a=1&&b=2&d&=&c=3%20&e=", NULL}; static const char *post[] = {
"a=1&&b=2&d&=&c=3%20&e=",
"q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
NULL
};
char buf[20]; char buf[20];
ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1); ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '1' && buf[1] == '\0'); ASSERT(buf[0] == '1' && buf[1] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1); ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
ASSERT(buf[0] == '2' && buf[1] == '\0'); ASSERT(buf[0] == '2' && buf[1] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2); ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0'); ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0); ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
ASSERT(buf[0] == '\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]), "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]), "c", buf, 2) == -2);
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2); 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); ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
} }
static void test_set_throttle(void) { static void test_set_throttle(void) {
@@ -255,6 +291,95 @@ static void test_next_option(void) {
} }
} }
#ifdef USE_LUA
static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
const char *v, *var_name = "myVar";
char buf[100];
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
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)));
}
static void test_lua(void) {
static struct mg_connection conn;
static struct mg_context ctx;
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_info);
conn.content_len = conn.data_len - conn.request_len;
prepare_lua_environment(&conn, L);
ASSERT(lua_gettop(L) == 0);
check_lua_expr(L, "'hi'", "hi");
check_lua_expr(L, "request_info.request_method", "POST");
check_lua_expr(L, "request_info.uri", "/foo/bar");
check_lua_expr(L, "request_info.num_headers", "2");
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()");
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));
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
}
static void test_skip_quoted(void) {
char x[] = "a=1, b=2 c='hi \' there'", *s = x, *p;
p = skip_quoted(&s, ", ", ", ", 0);
ASSERT(p != NULL && !strcmp(p, "a=1"));
p = skip_quoted(&s, ", ", ", ", 0);
ASSERT(p != NULL && !strcmp(p, "b=2"));
// TODO(lsm): fix this
#if 0
p = skip_quoted(&s, "'", ", ", '\\');
p = skip_quoted(&s, "'", ", ", '\\');
printf("[%s]\n", p);
ASSERT(p != NULL && !strcmp(p, "hi ' there"));
#endif
}
int main(void) { int main(void) {
test_base64_encode(); test_base64_encode();
test_match_prefix(); test_match_prefix();
@@ -265,5 +390,11 @@ int main(void) {
test_mg_get_var(); test_mg_get_var();
test_set_throttle(); test_set_throttle();
test_next_option(); test_next_option();
test_user_data();
test_mg_stat();
#ifdef USE_LUA
test_lua();
#endif
test_skip_quoted();
return 0; return 0;
} }