mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22f8cb14d5 | ||
|
|
e28b3ca91d | ||
|
|
73d2b395d1 | ||
|
|
e556a487e0 | ||
|
|
5728fa1cea | ||
|
|
b461edabd9 | ||
|
|
c9e666babc | ||
|
|
18e64397fc | ||
|
|
3c8418d324 | ||
|
|
784a1580ce | ||
|
|
6a0ac69761 | ||
|
|
7c06279a8c | ||
|
|
aec6f85f55 | ||
|
|
52e3be5c58 | ||
|
|
01a664ad06 | ||
|
|
94933047a7 | ||
|
|
cd6005c86f | ||
|
|
6d14ed3a18 | ||
|
|
0fb5db59d3 | ||
|
|
20b274bb2b | ||
|
|
00b289a0f4 | ||
|
|
9818f9849b | ||
|
|
df1c23222e | ||
|
|
f18ac3e094 | ||
|
|
d8596fae27 | ||
|
|
85209ac4fa | ||
|
|
a58e82b301 | ||
|
|
35f485558b | ||
|
|
93365fcbd2 | ||
|
|
e40a44ba70 | ||
|
|
1eed770238 | ||
|
|
511ef4137f | ||
|
|
479164129b | ||
|
|
08746115e2 | ||
|
|
587aad75c1 | ||
|
|
218428662e | ||
|
|
04fc209644 | ||
|
|
53bedd1eb5 | ||
|
|
a0b43ae8e0 | ||
|
|
866a7e2823 | ||
|
|
2055d41a9f | ||
|
|
1ef29c1b3b | ||
|
|
e95dbb90a7 | ||
|
|
cf18c932a4 | ||
|
|
a730afc44d | ||
|
|
94296c9697 | ||
|
|
667707df50 | ||
|
|
90b6fab3bb | ||
|
|
e47dbcbdc5 | ||
|
|
2a15f8d567 | ||
|
|
b5d397c74f | ||
|
|
3d6b373c8f | ||
|
|
ed236dddeb | ||
|
|
596fb1e88a | ||
|
|
ca0cd5040d | ||
|
|
5b84acbc14 | ||
|
|
c12838e2c3 | ||
|
|
fa3e6ce271 | ||
|
|
743f48b8e0 | ||
|
|
421c16b2bb | ||
|
|
3054a7f09e | ||
|
|
2de96bd54e | ||
|
|
f815c315e7 | ||
|
|
e59ee00154 | ||
|
|
b07745ab50 | ||
|
|
2468a8fb38 | ||
|
|
ed38a4102e | ||
|
|
b1c66f6544 | ||
|
|
8f79a0564f | ||
|
|
2e93cde929 | ||
|
|
e53d05637a | ||
|
|
9d75d35365 | ||
|
|
2ea8d85cff | ||
|
|
ea81a22529 | ||
|
|
526f3aa31c | ||
|
|
62d66201c9 | ||
|
|
f32e14478f | ||
|
|
ca019799b9 | ||
|
|
c6fb9ac3d0 | ||
|
|
c80a88a407 | ||
|
|
f2e19ca3ff | ||
|
|
16c463d2e9 | ||
|
|
6a99518048 | ||
|
|
3b242ec68b | ||
|
|
7409166409 | ||
|
|
ca66b84f7d | ||
|
|
dc1c732f11 | ||
|
|
61148f1323 | ||
|
|
136e3e353d | ||
|
|
c70b80e4e1 | ||
|
|
cba4c9d85d | ||
|
|
3e588065dc | ||
|
|
35cb27dbe6 | ||
|
|
d6b9f76e9c | ||
|
|
a9d51c0e87 | ||
|
|
4b163232e3 | ||
|
|
e003236a03 | ||
|
|
de7745370c | ||
|
|
21786eec8e | ||
|
|
036b213b2b | ||
|
|
61f5f0b725 | ||
|
|
d864d72470 | ||
|
|
7ca5962d2d | ||
|
|
8ce1562f0d | ||
|
|
49d9016275 | ||
|
|
cdc7380d00 | ||
|
|
e645b33b6c | ||
|
|
30f71a8bc4 | ||
|
|
faa030385c | ||
|
|
feb3b5db96 | ||
|
|
508b67d6f5 | ||
|
|
133aec777d | ||
|
|
389ed4e099 | ||
|
|
bc40aa5442 | ||
|
|
5fb9aca2e1 | ||
|
|
c68df310b8 | ||
|
|
70ce6c5ad3 | ||
|
|
460738f11b | ||
|
|
a09ec8988e | ||
|
|
c46aa050ae | ||
|
|
348db16245 | ||
|
|
c0ca6a53be | ||
|
|
e58d622d5f | ||
|
|
bcb294620f | ||
|
|
15937a9c5c | ||
|
|
fa160cb9a3 | ||
|
|
137d6484e2 | ||
|
|
e7c34c26cb | ||
|
|
dfb7d928c2 | ||
|
|
4bb9faaf50 | ||
|
|
a856971b6d | ||
|
|
4a88ad5ca9 | ||
|
|
17ecb0a459 | ||
|
|
afd39abc6f | ||
|
|
4babcff8de | ||
|
|
17d48b1d04 | ||
|
|
6ab20ed9a3 | ||
|
|
a0e5494569 | ||
|
|
f62c733990 | ||
|
|
d283e3167f | ||
|
|
94050d44e4 | ||
|
|
dee97cbf22 | ||
|
|
be01471968 | ||
|
|
a74c44c463 | ||
|
|
81e53d8375 | ||
|
|
ca626abcac | ||
|
|
b5e7730ac7 | ||
|
|
33b920ed2c | ||
|
|
b5b234fab2 | ||
|
|
af89d2446b | ||
|
|
791ad49651 | ||
|
|
063b26fece | ||
|
|
add079a949 | ||
|
|
6e59ad8a0d | ||
|
|
f4862b646d | ||
|
|
91fa767efb | ||
|
|
5e78711467 | ||
|
|
b4a622cfd6 | ||
|
|
8c118bf4cc | ||
|
|
728ebdaafa | ||
|
|
3a186976d7 | ||
|
|
2e232b3304 | ||
|
|
d72645bf91 | ||
|
|
7d5bcec92d | ||
|
|
215ea28db0 | ||
|
|
9b76d60b38 | ||
|
|
63ccf8199f | ||
|
|
6bc6022aa9 | ||
|
|
b68db66e97 | ||
|
|
94e1c5a41b | ||
|
|
ab16220861 | ||
|
|
c0eb3e56b4 | ||
|
|
5591aa5a0d | ||
|
|
6f946f5eea | ||
|
|
39cd5b8323 | ||
|
|
ca96db714a | ||
|
|
a7f97bf2f2 | ||
|
|
e579355b01 | ||
|
|
cfbaf7f1ae | ||
|
|
ce26234ace | ||
|
|
c28dc45d4c | ||
|
|
98b6c5e16a | ||
|
|
1d9d1111e1 | ||
|
|
2a0b55b30b | ||
|
|
1e3b167208 | ||
|
|
f7821b757c | ||
|
|
c352486abb | ||
|
|
4120b9bcc0 | ||
|
|
f7725f2eeb | ||
|
|
be06c3620c | ||
|
|
272644e39f | ||
|
|
55ef3afea4 | ||
|
|
9e07c742ff | ||
|
|
cd2e50c2c4 | ||
|
|
fdef2b8643 | ||
|
|
ce55924a02 | ||
|
|
fbca54bcaf | ||
|
|
41c25d60d6 | ||
|
|
a9b5bb351f | ||
|
|
51ed3efcd1 | ||
|
|
67cffc8db5 | ||
|
|
72abaa043b | ||
|
|
b3a511f9c5 | ||
|
|
8d1f6377c4 | ||
|
|
22dddc2eeb | ||
|
|
daab527743 | ||
|
|
dd49c0791f | ||
|
|
4b539d4b95 | ||
|
|
5eabfe5da2 | ||
|
|
36b7c8d60d | ||
|
|
aa37b728ad | ||
|
|
62085e5215 | ||
|
|
d036f8a3f0 | ||
|
|
fb5e2b8c2f | ||
|
|
4505b00733 | ||
|
|
c485b8264e | ||
|
|
8edd710c00 | ||
|
|
3984e9f5cc | ||
|
|
d4fa794c83 | ||
|
|
3892e0199e | ||
|
|
67f5de624e | ||
|
|
10234b07c4 | ||
|
|
36a83e1452 | ||
|
|
34df4ec1fd | ||
|
|
69cb94f3fb | ||
|
|
7204d71136 | ||
|
|
283f590349 | ||
|
|
27735f293b | ||
|
|
a560334fbb | ||
|
|
cd1707034c | ||
|
|
5ce1fc3c2a | ||
|
|
c78fe425a0 | ||
|
|
a58bb71c19 | ||
|
|
fc9a9593d2 | ||
|
|
eb1fdf56e6 | ||
|
|
ab960df8a9 | ||
|
|
08048bd5bb | ||
|
|
800517ac16 | ||
|
|
690eff4fa2 | ||
|
|
e47b5b775a | ||
|
|
c15e2af897 | ||
|
|
d8536b011c | ||
|
|
a2239c2b4b | ||
|
|
d3c02331d4 | ||
|
|
d707841166 | ||
|
|
affd82a885 | ||
|
|
8322c82790 | ||
|
|
08708435a7 | ||
|
|
1c6036c3ac | ||
|
|
6c3cb6f3eb | ||
|
|
a123874ac5 | ||
|
|
7931929a42 | ||
|
|
cc4a4fb2ab | ||
|
|
3d211d89c5 | ||
|
|
fbf1ccee45 | ||
|
|
0bd43bcae3 | ||
|
|
b809665cad | ||
|
|
463b49f1bd | ||
|
|
a881aad5c6 | ||
|
|
aae0995cd2 | ||
|
|
26b3f227d9 | ||
|
|
665770d602 | ||
|
|
fcafaa73d6 | ||
|
|
079b9ec2c1 | ||
|
|
7bd9a85897 | ||
|
|
2efc5d9fce | ||
|
|
1cb094e059 | ||
|
|
27fe9c8f01 | ||
|
|
9178fbd153 | ||
|
|
b2b43ab0cd | ||
|
|
1415d187eb | ||
|
|
a65078b858 | ||
|
|
7107afae5c | ||
|
|
f6a807c5a4 | ||
|
|
47b11dd19e | ||
|
|
61cdc93405 | ||
|
|
7a52bd643c | ||
|
|
63e8052e4b | ||
|
|
782d0cda46 | ||
|
|
72803eb7f8 | ||
|
|
c7fd221ab7 | ||
|
|
492185e35f | ||
|
|
845d7b08e1 | ||
|
|
49bac31d2d | ||
|
|
9baf2d4ba4 | ||
|
|
1becd26a8f | ||
|
|
ffa983552f | ||
|
|
13d0b67c00 | ||
|
|
bda8e90733 | ||
|
|
fed2057a33 | ||
|
|
a27f62f89f | ||
|
|
d53902844c | ||
|
|
9870b80c65 | ||
|
|
cde25a8b82 | ||
|
|
1b7f036199 | ||
|
|
b80157b7ea | ||
|
|
f9311f0b30 | ||
|
|
bdd933f597 | ||
|
|
ee55d38b55 | ||
|
|
62162ff3e4 | ||
|
|
98f66c8867 | ||
|
|
c132191cad | ||
|
|
d73aa1ad09 | ||
|
|
d5bc58c38d | ||
|
|
51a80e4844 | ||
|
|
54f15a8fe3 | ||
|
|
ce0826e285 | ||
|
|
b21a8b2193 | ||
|
|
8bb49642da | ||
|
|
3f78df86ac | ||
|
|
c30f7a5258 | ||
|
|
7ed94d1918 | ||
|
|
dc513a9a15 | ||
|
|
8ce6cf61b7 | ||
|
|
e63f7fe33a | ||
|
|
b589e0cdf4 | ||
|
|
2b87f909ce | ||
|
|
ec7d0b1abd | ||
|
|
5bb3ede46d | ||
|
|
5105717f78 | ||
|
|
9d1e356709 | ||
|
|
3d7192d9fa | ||
|
|
1b3cbb17c1 | ||
|
|
f1f03e4378 | ||
|
|
625a2a3652 | ||
|
|
33a31d9308 | ||
|
|
5e9bd1f05e | ||
|
|
75d9a6c844 | ||
|
|
179761fd99 | ||
|
|
b783813e54 | ||
|
|
4b29689a42 | ||
|
|
9c4853471c | ||
|
|
81e21a3a0b | ||
|
|
bec6ea00ea | ||
|
|
ed32e3be0f | ||
|
|
83fae879bf | ||
|
|
2169e6d77f | ||
|
|
850e0a5922 | ||
|
|
322ed3cc6f | ||
|
|
6d4d53c247 | ||
|
|
e6f8d40942 | ||
|
|
7e051206ad | ||
|
|
76d913796c | ||
|
|
4c2609df2c | ||
|
|
f79bd71fd4 | ||
|
|
5d4f5b6000 | ||
|
|
fe7c245a6b | ||
|
|
f24bebaca8 | ||
|
|
acf72a6109 |
No files matched your search
@@ -1,19 +1,16 @@
|
||||
Copyright (c) 2004-2010 Sergey Lyubka
|
||||
Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
Copyright (c) 2013 Cesanta Software Limited
|
||||
All rights reserved
|
||||
|
||||
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:
|
||||
This code is dual-licensed: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation. For the terms of this
|
||||
license, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
You are free to use this code under the terms of the GNU General
|
||||
Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
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.
|
||||
Alternatively, you can license this code under a commercial
|
||||
license, as set out in <http://cesanta.com/products.html>.
|
||||
@@ -1,161 +0,0 @@
|
||||
# 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)
|
||||
# -DHAVE_MD5 - use system md5 library (-2kb)
|
||||
# -DNDEBUG - strip off all debug code (-5kb)
|
||||
# -DDEBUG - build debug version (very noisy) (+7kb)
|
||||
# -DNO_CGI - disable CGI support (-5kb)
|
||||
# -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
|
||||
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
|
||||
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
|
||||
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
|
||||
|
||||
|
||||
##########################################################################
|
||||
### 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
|
||||
|
||||
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 \
|
||||
$(LUA)/lmem.c $(LUA)/lobject.c $(LUA)/lopcodes.c \
|
||||
$(LUA)/lparser.c $(LUA)/lstate.c $(LUA)/lstring.c \
|
||||
$(LUA)/ltable.c $(LUA)/ltm.c $(LUA)/lundump.c \
|
||||
$(LUA)/lvm.c $(LUA)/lzio.c $(LUA)/lauxlib.c \
|
||||
$(LUA)/lbaselib.c $(LUA)/lbitlib.c $(LUA)/lcorolib.c \
|
||||
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
|
||||
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.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
|
||||
|
||||
cyassl.lib:
|
||||
$(CL) /Fo$(CYA)/ $(CYA_SOURCES) $(CYAFL) $(DEF)
|
||||
$(MSVC)/bin/lib $(CYA)/*.obj /out:$@
|
||||
|
||||
lua.lib:
|
||||
$(CL) /c /Fo$(LUA)/ $(LUA_SOURCES)
|
||||
$(MSVC)/bin/lib $(LUA_SOURCES:%.c=%.obj) /out:$@
|
||||
|
||||
windows: cyassl.lib lua.lib
|
||||
$(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
|
||||
|
||||
# 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
|
||||
|
||||
# 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 ./build/res.rc ./build/res.o
|
||||
$(CC) $(CYGWINOPT) mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(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
|
||||
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
|
||||
|
||||
clean:
|
||||
cd examples && $(MAKE) clean
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
|
||||
$(PROG).exe *.dll *.lib build/res.o build/res.RES *.dSYM
|
||||
@@ -1,56 +1,57 @@
|
||||
Project Mission
|
||||
---------------
|
||||
# Project Mission
|
||||
|
||||
Project mission is to provide simple, functional, embeddable web server to
|
||||
make it easy for application and device developers to implement web interface
|
||||
for their application and devices, and to offer a simple web development
|
||||
environment.
|
||||
Project mission is to provide easy to use, powerful, embeddable web server.
|
||||
|
||||
Overview
|
||||
--------
|
||||
# Downloads
|
||||
|
||||
To accomplish it's mission, Mongoose keeps balance on functionality and
|
||||
Sources, binaries and bundles are available for download at
|
||||
[mongoose downloads](http://cesanta.com/downloads.html).
|
||||
|
||||
# Overview
|
||||
|
||||
Mongoose keeps the balance between functionality and
|
||||
simplicity by carefully selected list of features:
|
||||
|
||||
- Liberal, commercial-friendly
|
||||
[MIT license](http://en.wikipedia.org/wiki/MIT_License)
|
||||
- Works on Windows, Mac, UNIX, iPhone, Android, and many other platforms
|
||||
- Scripting and database support (Lua Server Pages + Sqlite, see
|
||||
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) ),
|
||||
which provides ready to go, powerful web development platform in
|
||||
one single-click executable with **no dependencies**: forget LAMP!
|
||||
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
|
||||
- Lua server pages (PHP-like functionality using Lua), see
|
||||
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
||||
- Excluding files from serving by URI pattern (file blacklist)
|
||||
- Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
||||
- 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 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.
|
||||
- Embedding examples:
|
||||
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)
|
||||
- Extensive documentation in form of
|
||||
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||
- HTTP client capable of sending arbitrary HTTP/HTTPS requests
|
||||
- [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||
|
||||
Note that Windows and MacOS binaries have following 3rd party software
|
||||
compiled in:
|
||||
<a href="http://wolfssl.com">WolfSSL lightweight SSL library</a>,
|
||||
<a href="http://sqlite.org">SQLite embedded database</a>,
|
||||
<a href="http://lua.org">Lua embedded scripting engine</a>.
|
||||
|
||||
Questions can be asked at
|
||||
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users)
|
||||
[mongoose-users@googlegroups.com](http://groups.google.com/group/mongoose-users)
|
||||
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
|
||||
community for free is
|
||||
[Super Light Regular Expression library](http://code.google.com/p/slre).
|
||||
[](http://githalytics.com/valenok/mongoose)
|
||||
|
||||
|
||||
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!
|
||||
# License
|
||||
|
||||
Mongoose is dual-licensed. It is available under the terms of
|
||||
[GNU GPL v.2](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) license
|
||||
license for free, or under the terms of standard commercial license provided
|
||||
by [Cesanta Software](http://cesanta.com).
|
||||
|
||||
[Super Light Regular Expression library](https://github.com/cesanta/slre) is
|
||||
another project by Cesanta Software, developed with the same philosophy
|
||||
of functionality and simplicity.
|
||||
+471
-131
@@ -1,15 +1,15 @@
|
||||
|
||||
**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.
|
||||
|
||||
On Windows, mongoose iconifies itself to the system tray icon when started.
|
||||
Right-click on the icon pops up a menu, where it is possible to stop
|
||||
mongoose, or configure it, or install it as Windows service.
|
||||
mongoose, or configure it, or install it as Windows service. The easiest way
|
||||
to share a folder on Windows is to copy `mongoose.exe` to a folder,
|
||||
double-click the exe, and launch a browser at
|
||||
[http://localhost:8080](http://localhost:8080). Note that 'localhost' should
|
||||
be changed to a machine's name if a folder is accessed from other computer.
|
||||
|
||||
On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in
|
||||
terminal, optionally followed by configuration parameters
|
||||
@@ -22,12 +22,13 @@ When started, mongoose first searches for the configuration file.
|
||||
If configuration file is specified explicitly in the command line, i.e.
|
||||
`mongoose path_to_config_file`, then specified configuration file is used.
|
||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
||||
where binary is located, and use it.
|
||||
where binary is located, and use it. Configuration file can be absent.
|
||||
|
||||
|
||||
Configuration file is a sequence of lines, each line containing
|
||||
command line argument name and it's value. Empty lines, and lines beginning
|
||||
with `#`, are ignored. Here is the example of `mongoose.conf` file:
|
||||
|
||||
# mongoose.conf file
|
||||
document_root c:\www
|
||||
listening_ports 8080,8043s
|
||||
ssl_certificate c:\mongoose\ssl_cert.pem
|
||||
@@ -39,165 +40,504 @@ For example, if `mongoose.conf` has line
|
||||
`document_root /var/www`, and mongoose has been started as
|
||||
`mongoose -document_root /etc`, then `/etc` directory will be served as
|
||||
document root, because command line options take priority over
|
||||
configuration file.
|
||||
configuration file. Configuration options section below provide a good
|
||||
overview of Mongoose features.
|
||||
|
||||
Note that configuration options on the command line must start with `-`,
|
||||
but their names are the same as in the config file. All option names are
|
||||
listed in the next section. Thus, the following two setups are equivalent:
|
||||
|
||||
# Using command line arguments
|
||||
$ mongoose -listening_ports 1234 -document_root /var/www
|
||||
|
||||
# Using config file
|
||||
$ cat mongoose.conf
|
||||
listening_ports 1234
|
||||
document_root /var/www
|
||||
$ mongoose
|
||||
|
||||
Mongoose can also be used to modify `.htpasswd` passwords file:
|
||||
|
||||
mongoose -A <htpasswd_file> <realm> <user> <passwd>
|
||||
|
||||
Usage Examples
|
||||
--------------
|
||||
Unlike other web servers, mongoose does not require CGI scripts be located in
|
||||
a special directory. CGI scripts can be anywhere. CGI (and SSI) files are
|
||||
recognized by the file name pattern. Mongoose uses shell-like glob
|
||||
patterns. Pattern match starts at the beginning of the string, so essentially
|
||||
patterns are prefix patterns. Syntax is as follows:
|
||||
|
||||
- How to share a Windows folder: copy mongoose executable to a folder and
|
||||
double-click the executable. The folder should be accessible via
|
||||
[http://localhost:8080](http://localhost:8080) in any browser.
|
||||
** Matches everything
|
||||
* Matches everything but slash character, '/'
|
||||
? Matches any character
|
||||
$ Matches the end of the string
|
||||
| Matches if pattern on the left side or the right side matches.
|
||||
|
||||
- How to start mongoose at UNIX startup time in daemon mode, serving
|
||||
directory `/var/www`: put this line in the system startup script:
|
||||
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
||||
All other characters in the pattern match themselves. Examples:
|
||||
|
||||
- How to serve user home directories using URL rewrite:
|
||||
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
|
||||
**.cgi$ Any string that ends with .cgi
|
||||
/foo Any string that begins with /foo
|
||||
**a$|**b$ Any string that ends with a or b
|
||||
|
||||
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.
|
||||
Below is a list of configuration options Mongoose understands. Every option
|
||||
is followed by it's default value. If default value is not present, then
|
||||
it is empty.
|
||||
|
||||
-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
|
||||
certain directory, use e.g. "-C /cgi-bin/**.cgi". Default:
|
||||
"**.cgi$|**.pl$|**.php$"
|
||||
### cgi_pattern `**.cgi$|**.pl$|**.php$`
|
||||
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
||||
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
|
||||
matched against the pattern, not the URI.
|
||||
|
||||
-E 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: ""
|
||||
### 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 name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`.
|
||||
|
||||
-G put_delete_passwords_file
|
||||
PUT and DELETE passwords file. This must be specified if PUT or
|
||||
DELETE methods are used. Default: ""
|
||||
### put\_delete\_auth\_file
|
||||
Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests
|
||||
will fail.
|
||||
|
||||
-I cgi_interpreter
|
||||
Use 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:
|
||||
"".
|
||||
### cgi_interpreter
|
||||
Path to an executable to use as CGI interpreter for __all__ CGI scripts
|
||||
regardless script extension. If this option is not set (which is a default),
|
||||
Mongoose looks at first line of a CGI script,
|
||||
[shebang line](http://en.wikipedia.org/wiki/Shebang_(Unix\)), for an interpreter.
|
||||
|
||||
-P protect_uri
|
||||
Comma separated list of URI=PATH pairs, specifying that given
|
||||
URIs must be protected with respected password files. Default: ""
|
||||
For example, if both PHP and perl CGIs are used, then
|
||||
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
||||
respective CGI scripts. Note that paths should be either full file paths,
|
||||
or file paths relative to the current working directory of mongoose server.
|
||||
If mongoose is started by mouse double-click on Windows, current working
|
||||
directory is a directory where mongoose executable is located.
|
||||
|
||||
-R authentication_domain
|
||||
Authorization realm. Default: "mydomain.com"
|
||||
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||
`cgi_interpreter` can be set to the path to `php-cgi.exe` executable and
|
||||
shebang line in the CGI scripts can be omitted.
|
||||
Note that PHP scripts must use `php-cgi.exe` executable, not `php.exe`.
|
||||
|
||||
-S 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$"
|
||||
### protect_uri
|
||||
Comma separated list of URI=PATH pairs, specifying that given
|
||||
URIs must be protected with respected password files. Paths must be full
|
||||
file paths.
|
||||
|
||||
-T throttle
|
||||
Limit download speed for clients. 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: ""
|
||||
### authentication_domain `mydomain.com`
|
||||
Authorization realm used in `.htpasswd` authorization.
|
||||
|
||||
-a access_log_file
|
||||
Access log file. Default: "", no logging is done.
|
||||
### ssi_pattern `**.shtml$|**.shtm$`
|
||||
All files that match `ssi_pattern` are treated as SSI.
|
||||
|
||||
-d enable_directory_listing
|
||||
Enable/disable directory listing. Default: "yes"
|
||||
Server Side Includes (SSI) is a simple interpreted server-side scripting
|
||||
language which is most commonly used to include the contents of a file into
|
||||
a web page. It can be useful when it is desirable to include a common piece
|
||||
of code throughout a website, for example, headers and footers.
|
||||
|
||||
-e error_log_file
|
||||
Error log file. Default: "", no errors are logged.
|
||||
In order for a webpage to recognize an SSI-enabled HTML file, the filename
|
||||
should end with a special extension, by default the extension should be
|
||||
either `.shtml` or `.shtm`.
|
||||
|
||||
-g 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: ""
|
||||
Unknown SSI directives are silently ignored by mongoose. Currently, two SSI
|
||||
directives are supported, `<!--#include ...>` and
|
||||
`<!--#exec "command">`. Note that `<!--#include ...>` directive supports
|
||||
three path specifications:
|
||||
|
||||
-i index_files
|
||||
Comma-separated list of files to be treated as directory index
|
||||
files. Default: "index.html,index.htm,index.cgi"
|
||||
<!--#include virtual="path"> Path is relative to web server root
|
||||
<!--#include abspath="path"> Path is absolute or relative to
|
||||
web server working dir
|
||||
<!--#include file="path">, Path is relative to current document
|
||||
<!--#include "path">
|
||||
|
||||
-l 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: ""
|
||||
The `include` directive may be used to include the contents of a file or the
|
||||
result of running a CGI script. The `exec` directive is used to execute a
|
||||
command on a server, and show command's output. Example:
|
||||
|
||||
-m extra_mime_types
|
||||
Extra mime types to recognize, in form "extension1=type1,exten-
|
||||
sion2=type2,...". Extension must include dot. Example: "mongoose
|
||||
-m .cpp=plain/text,.java=plain/text". Default: ""
|
||||
<!--#exec "ls -l" -->
|
||||
|
||||
-p 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"
|
||||
For more information on Server Side Includes, take a look at the Wikipedia:
|
||||
[Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes)
|
||||
|
||||
-r document_root
|
||||
Location of the WWW root directory. Default: "."
|
||||
### throttle
|
||||
Limit download speed for clients. `throttle` is a comma-separated
|
||||
list of key=value pairs, where key could be:
|
||||
|
||||
-s ssl_certificate
|
||||
Location of SSL certificate file. Default: ""
|
||||
* limit speed for all connections
|
||||
x.x.x.x/mask limit speed for specified subnet
|
||||
uri_prefix_pattern limit speed for given URIs
|
||||
|
||||
-t num_threads
|
||||
Number of worker threads to start. Default: "10"
|
||||
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. Examples:
|
||||
|
||||
-u run_as_user
|
||||
Switch to given user's credentials after startup. Default: ""
|
||||
*=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second,
|
||||
but give connections from 10.0.0.0/8 subnet
|
||||
unlimited speed
|
||||
|
||||
-w url_rewrite_patterns
|
||||
Comma-separated list of URL rewrites in the form of "pattern=sub-
|
||||
stitution,..." If the "pattern" matches some prefix of the
|
||||
requested URL, then matched prefix gets substituted with "substi-
|
||||
tution". For example, "-w /con-
|
||||
fig=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi" will
|
||||
serve all URLs that start with "/config" from the "/etc" direc-
|
||||
tory, and call handle_doc.cgi script for .doc and .rtf file
|
||||
requests. If some pattern matches, no further matching/substitu-
|
||||
tion is performed (first matching pattern wins). Use full paths
|
||||
in substitutions. Default: ""
|
||||
/downloads/=5k limit accesses to all URIs in `/downloads/` to
|
||||
5 kilobytes per secods. All other accesses are unlimited
|
||||
|
||||
-x hide_files_patterns
|
||||
A prefix pattern for the files to hide. Files that match the pat-
|
||||
tern will not show up in directory listing and return 404 Not
|
||||
Found if requested. Default: ""
|
||||
### access\_log\_file
|
||||
Path to a file for access logs. Either full path, or relative to current
|
||||
working directory. If absent (default), then accesses are not logged.
|
||||
|
||||
```
|
||||
### error\_log\_file
|
||||
Path to a file for error logs. Either full path, or relative to current
|
||||
working directory. If absent (default), then errors are not logged.
|
||||
|
||||
Common Problems
|
||||
---------------
|
||||
### enable\_directory\_listing `yes`
|
||||
Enable directory listing, either `yes` or `no`.
|
||||
|
||||
Embedding
|
||||
---------
|
||||
### enable\_keep\_alive `no`
|
||||
Enable connection keep alive, either `yes` or `no`.
|
||||
|
||||
Other Resources
|
||||
---------------
|
||||
Experimental feature. Allows clients to reuse TCP connection for
|
||||
subsequent HTTP requests, which improves performance.
|
||||
For this to work when using request handlers it's important to add the correct
|
||||
Content-Length HTTP header for each request. If this is forgotten the client
|
||||
will time out.
|
||||
|
||||
|
||||
### global\_auth\_file
|
||||
Path to a global passwords file, either full path or relative to the current
|
||||
working directory. If set, per-directory `.htpasswd` files are ignored,
|
||||
and all requests are authorised against that file.
|
||||
|
||||
The file has to include the realm set through `authentication_domain` and the password in digest format:
|
||||
|
||||
user:realm:digest
|
||||
test:test.com:ce0220efc2dd2fad6185e1f1af5a4327
|
||||
|
||||
(e.g. use [this generator](http://www.askapache.com/online-tools/htpasswd-generator))
|
||||
|
||||
### index_files `index.html,index.htm,index.cgi,index.shtml,index.php`
|
||||
Comma-separated list of files to be treated as directory index
|
||||
files.
|
||||
|
||||
### access\_control\_list
|
||||
An Access Control List (ACL) allows restrictions to be put on the list of IP
|
||||
addresses which have access to the web server. In the case of the Mongoose
|
||||
web server, the ACL is a comma separated list of IP subnets, where each
|
||||
subnet is prepended by either a `-` or a `+` sign. A plus sign means allow,
|
||||
where a minus sign means deny. If a subnet mask is omitted, such as `-1.2.3.4`,
|
||||
this means to deny only that single IP address.
|
||||
|
||||
Subnet masks may vary from 0 to 32, inclusive. The default setting is to allow
|
||||
all accesses. On each request the full list is traversed, and
|
||||
the last match wins. Examples:
|
||||
|
||||
-0.0.0.0/0,+192.168/16 deny all acccesses, only allow 192.168/16 subnet
|
||||
|
||||
To learn more about subnet masks, see the
|
||||
[Wikipedia page on Subnetwork](http://en.wikipedia.org/wiki/Subnetwork)
|
||||
|
||||
### extra\_mime\_types
|
||||
Extra mime types to recognize, in form `extension1=type1,exten-
|
||||
sion2=type2,...`. Extension must include dot. Example:
|
||||
`.cpp=plain/text,.java=plain/text`
|
||||
|
||||
### listening_ports `8080`
|
||||
Comma-separated list of ports to listen on. If the port is SSL, a
|
||||
letter `s` must be appeneded, for example, `80,443s` will open
|
||||
port 80 and port 443, and connections on port 443 will be SSL-ed.
|
||||
For non-SSL ports, it is allowed to append letter `r`, meaning 'redirect'.
|
||||
Redirect ports will redirect all their traffic to the first configured
|
||||
SSL port. For example, if `listening_ports` is `80r,443s`, then all
|
||||
HTTP traffic coming at port 80 will be redirected to HTTPS port 443.
|
||||
|
||||
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 `127.0.0.1:80,443s`.
|
||||
|
||||
### document_root `.`
|
||||
A directory to serve. By default, currect directory is served. Current
|
||||
directory is commonly referenced as dot (`.`).
|
||||
|
||||
### ssl_certificate
|
||||
Path to SSL certificate file. This option is only required when at least one
|
||||
of the `listening_ports` is SSL. The file must be in PEM format,
|
||||
and it must have both private key and certificate, see for example
|
||||
[ssl_cert.pem](https://github.com/valenok/mongoose/blob/master/build/ssl_cert.pem)
|
||||
|
||||
### num_threads `50`
|
||||
Number of worker threads. Mongoose handles each incoming connection in a
|
||||
separate thread. Therefore, the value of this option is effectively a number
|
||||
of concurrent HTTP connections Mongoose can handle.
|
||||
|
||||
### run\_as\_user
|
||||
Switch to given user credentials after startup. Usually, this option is
|
||||
required when mongoose needs to bind on privileged port on UNIX. To do
|
||||
that, mongoose needs to be started as root. But running as root is a bad idea,
|
||||
therefore this option can be used to drop privileges. Example:
|
||||
|
||||
mongoose -listening_ports 80 -run_as_user nobody
|
||||
|
||||
### request\_timeout\_ms `30000`
|
||||
Timeout for network read and network write operations, in milliseconds.
|
||||
If client intends to keep long-running connection, either increase this value
|
||||
or use keep-alive messages.
|
||||
|
||||
|
||||
### url\_rewrite\_patterns
|
||||
Comma-separated list of URL rewrites in the form of
|
||||
`uri_pattern=file_or_directory_path`. When Mongoose receives the request,
|
||||
it constructs the file name to show by combining `document_root` and the URI.
|
||||
However, if the rewrite option is used and `uri_pattern` matches the
|
||||
requested URI, then `document_root` is ignored. Insted,
|
||||
`file_or_directory_path` is used, which should be a full path name or
|
||||
a path relative to the web server's current working directory. Note that
|
||||
`uri_pattern`, as all mongoose patterns, is a prefix pattern.
|
||||
|
||||
This makes it possible to serve many directories outside from `document_root`,
|
||||
redirect all requests to scripts, and do other tricky things. For example,
|
||||
to redirect all accesses to `.doc` files to a special script, do:
|
||||
|
||||
mongoose -url_rewrite_patterns **.doc$=/path/to/cgi-bin/handle_doc.cgi
|
||||
|
||||
Or, to imitate user home directories support, do:
|
||||
|
||||
mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/
|
||||
|
||||
### hide\_files\_patterns
|
||||
A 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. Pattern
|
||||
must be for a file name only, not including directory name. Example:
|
||||
|
||||
mongoose -hide_files_patterns secret.txt|even_more_secret.txt
|
||||
|
||||
# Lua Server Pages
|
||||
Pre-built Windows and Mac mongoose binaries have built-in Lua Server Pages
|
||||
support. That means it is possible to write PHP-like scripts with mongoose,
|
||||
using Lua programming language instead of PHP. Lua is known
|
||||
for it's speed and small size. Mongoose uses Lua version 5.2.1, the
|
||||
documentation for it can be found at
|
||||
[Lua 5.2 reference manual](http://www.lua.org/manual/5.2/).
|
||||
|
||||
To create a Lua Page, make sure a file has `.lp` extension. For example,
|
||||
let's say it is going to be `my_page.lp`. The contents of the file, just like
|
||||
with PHP, is HTML with embedded Lua code. Lua code must be enclosed in
|
||||
`<? ?>` blocks, and can appear anywhere on the page. For example, to
|
||||
print current weekday name, one can write:
|
||||
|
||||
<p>
|
||||
<span>Today is:</span>
|
||||
<? mg.write(os.date("%A")) ?>
|
||||
</p>
|
||||
|
||||
Note that this example uses function `mg.write()`, which prints data to the
|
||||
web page. Using function `mg.write()` is the way to generate web content from
|
||||
inside Lua code. In addition to `mg.write()`, all standard library functions
|
||||
are accessible from the Lua code (please check reference manual for details),
|
||||
and also information about the request is available in `mg.request_info` object,
|
||||
like request method, all headers, etcetera. Please refer to
|
||||
`struct mg_request_info` definition in
|
||||
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
|
||||
to see what kind of information is present in `mg.request_info` object. Also,
|
||||
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp) and
|
||||
[prime_numbers.lp](https://github.com/valenok/mongoose/blob/master/examples/lua/prime_numbers.lp)
|
||||
contains some example code that uses `request_info` and other functions(form submitting for example).
|
||||
|
||||
Mongoose exports the following to the Lua server page:
|
||||
|
||||
mg.read() -- reads a chunk from POST data, returns it as a string
|
||||
mg.write(str) -- writes string to the client
|
||||
mg.include(path) -- sources another Lua file
|
||||
mg.redirect(uri) -- internal redirect to a given URI
|
||||
mg.onerror(msg) -- error handler, can be overridden
|
||||
mg.version -- a string that holds Mongoose version
|
||||
mg.request_info -- a table with request information
|
||||
|
||||
-- Connect to the remote TCP server. This function is an implementation
|
||||
-- of simple socket interface. It returns a socket object with three
|
||||
-- methods: send, recv, close, which are synchronous (blocking).
|
||||
-- connect() throws an exception on connection error.
|
||||
connect(host, port, use_ssl)
|
||||
|
||||
-- Example of using connect() interface:
|
||||
local host = 'code.google.com' -- IP address or domain name
|
||||
local ok, sock = pcall(connect, host, 80, 1)
|
||||
if ok then
|
||||
sock:send('GET /p/mongoose/ HTTP/1.0\r\n' ..
|
||||
'Host: ' .. host .. '\r\n\r\n')
|
||||
local reply = sock:recv()
|
||||
sock:close()
|
||||
-- reply now contains the web page https://code.google.com/p/mongoose
|
||||
end
|
||||
|
||||
|
||||
**IMPORTANT: Mongoose does not send HTTP headers for Lua pages. Therefore,
|
||||
every Lua Page must begin with HTTP reply line and headers**, like this:
|
||||
|
||||
<? print('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n') ?>
|
||||
<html><body>
|
||||
... the rest of the web page ...
|
||||
|
||||
To serve Lua Page, mongoose creates Lua context. That context is used for
|
||||
all Lua blocks within the page. That means, all Lua blocks on the same page
|
||||
share the same context. If one block defines a variable, for example, that
|
||||
variable is visible in the block that follows.
|
||||
|
||||
# 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`
|
||||
|
||||
- Mongoose fails to start. If Mongoose exits immediately when run, this
|
||||
usually indicates a syntax error in the configuration file
|
||||
(named `mongoose.conf` by default) or the command-line arguments.
|
||||
Syntax checking is omitted from Mongoose to keep its size low. However,
|
||||
the Manual should be of help. Note: the syntax changes from time to time,
|
||||
so updating the config file might be necessary after executable update.
|
||||
|
||||
- Embedding with OpenSSL on Windows might fail because of calling convention.
|
||||
To force Mongoose to use `__stdcall` convention, add `/Gz` compilation
|
||||
flag in Visual Studio compiler.
|
||||
|
||||
# Embedding
|
||||
Embedding Mongoose is easy. Copy
|
||||
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) and
|
||||
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)
|
||||
to your application's source tree and include them in the build. For
|
||||
example, your application's code lives in C file `my_app.c`, then on UNIX
|
||||
this command embeds Mongoose:
|
||||
|
||||
$ ls
|
||||
my_app.c mongoose.c mongoose.h
|
||||
$ gcc my_app.c mongoose.c -o my_app -ldl -pthread
|
||||
|
||||
Somewhere in the application code, call `mg_start()` to start the server.
|
||||
Pass configuration options and event handlers to `mg_start()`.
|
||||
Mongoose then calls handlers when certain events happen.
|
||||
For example, when new request arrives, Mongoose calls `begin_request`
|
||||
handler function to let user handle the request. In the handler, user code
|
||||
can get all information about the request -- parsed headers, etcetera.
|
||||
|
||||
Mongoose API is logically divided in three categories: server setup/shutdown
|
||||
functions, functions to be used by user-written event handlers, and
|
||||
convenience utility functions.
|
||||
|
||||
### Starting and stopping embedded web server
|
||||
To start the embedded web server, call `mg_start()`. To stop it, call
|
||||
`mg_stop()`.
|
||||
|
||||
// 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);
|
||||
int (*http_error)(struct mg_connection *, int status);
|
||||
};
|
||||
|
||||
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c)
|
||||
provides a minimalistic example.
|
||||
|
||||
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.
|
||||
|
||||
# Build on Android
|
||||
|
||||
This is a small guide to help you run mongoose on Android. Currently it is
|
||||
tested on the HTC Wildfire. If you have managed to run it on other devices
|
||||
as well, please comment or drop an email in the mailing list.
|
||||
Note : You dont need root access to run mongoose on Android.
|
||||
|
||||
- Download the source from the Downloads page.
|
||||
- Download the Android NDK from [http://developer.android.com/tools/sdk/ndk/index.html](http://developer.android.com/tools/sdk/ndk/index.html)
|
||||
- Run `/path-to-ndk/ndk-build -C /path-to-mongoose/build`
|
||||
That should generate mongoose/lib/armeabi/mongoose
|
||||
- Using the adb tool (you need to have Android SDK installed for that),
|
||||
push the generated mongoose binary to `/data/local` folder on device.
|
||||
- From adb shell, navigate to `/data/local` and execute `./mongoose`.
|
||||
- To test if the server is running fine, visit your web-browser and
|
||||
navigate to `http://127.0.0.1:8080` You should see the `Index of /` page.
|
||||
|
||||

|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
- `jni` stands for Java Native Interface. Read up on Android NDK if you want
|
||||
to know how to interact with the native C functions of mongoose in Android
|
||||
Java applications.
|
||||
- TODO: A Java application that interacts with the native binary or a
|
||||
shared library.
|
||||
|
||||
# Mongoose internals
|
||||
|
||||
Mongoose is multithreaded web server. `mg_start()` function allocates
|
||||
web server context (`struct mg_context`), which holds all information
|
||||
about web server instance:
|
||||
|
||||
- configuration options. Note that mongoose makes internal copies of
|
||||
passed options.
|
||||
- SSL context, if any
|
||||
- user-defined callbacks
|
||||
- opened listening sockets
|
||||
- a queue for accepted sockets
|
||||
- mutexes and condition variables for inter-thread synchronization
|
||||
|
||||
When `mg_start()` returns, all initialization is quaranteed to be complete
|
||||
(e.g. listening ports are opened, SSL is initialized, etc). `mg_start()` starts
|
||||
two threads: a master thread, that accepts new connections, and several
|
||||
worker threads, that process accepted connections. The number of worker threads
|
||||
is configurable via `num_threads` configuration option. That number puts a
|
||||
limit on number of simultaneous requests that can be handled by mongoose.
|
||||
|
||||
When master thread accepts new connection, a new accepted socket (described by
|
||||
`struct socket`) it placed into the accepted sockets queue,
|
||||
which has size of 20 (see [code](https://github.com/valenok/mongoose/blob/3892e0199e6ca9613b160535d9d107ede09daa43/mongoose.c#L486)). Any idle worker thread
|
||||
can grab accepted sockets from that queue. If all worker threads are busy,
|
||||
master thread can accept and queue up to 20 more TCP connections,
|
||||
filling up the queue.
|
||||
In the attempt to queue next accepted connection, master thread blocks
|
||||
until there is space in a queue. When master thread is blocked on a
|
||||
full queue, TCP layer in OS can also queue incoming connection.
|
||||
The number is limited by the `listen()` call parameter on listening socket,
|
||||
which is `SOMAXCONN` in case of Mongoose, and depends on a platform.
|
||||
|
||||
Worker threads are running in an infinite loop, which in simplified form
|
||||
looks something like this:
|
||||
|
||||
static void *worker_thread() {
|
||||
while (consume_socket()) {
|
||||
process_new_connection();
|
||||
}
|
||||
}
|
||||
|
||||
Function `consume_socket()` gets new accepted socket from the mongoose socket
|
||||
queue, atomically removing it from the queue. If the queue is empty,
|
||||
`consume_socket()` blocks and waits until new sockets are placed in a queue
|
||||
by the master thread. `process_new_connection()` actually processes the
|
||||
connection, i.e. reads the request, parses it, and performs appropriate action
|
||||
depending on a parsed request.
|
||||
|
||||
Master thread uses `poll()` and `accept()` to accept new connections on
|
||||
listening sockets. `poll()` is used to avoid `FD_SETSIZE` limitation of
|
||||
`select()`. Since there are only a few listening sockets, there is no reason
|
||||
to use hi-performance alternatives like `epoll()` or `kqueue()`. Worker
|
||||
threads use blocking IO on accepted sockets for reading and writing data.
|
||||
All accepted sockets have `SO_RCVTIMEO` and `SO_SNDTIMEO` socket options set
|
||||
(controlled by `request_timeout_ms` mongoose option, 30 seconds default) which
|
||||
specify read/write timeout on client connection.
|
||||
|
||||
# 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
@@ -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>
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
# This Makefile is part of Mongoose web server project,
|
||||
# https://github.com/valenok/mongoose
|
||||
#
|
||||
# This Makefile is GNU make compatible. You can get GNU Make from
|
||||
# http://gnuwin32.sourceforge.net/packages/make.htm
|
||||
#
|
||||
# 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)
|
||||
# -DNO_CGI - disable CGI support (-5kb)
|
||||
# -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
|
||||
# -DSSL_LIB=\"libssl.so.<version>\" - use system versioned SSL shared object
|
||||
# -DCRYPTO_LIB=\"libcrypto.so.<version>\" - use system versioned CRYPTO so
|
||||
# -DUSE_LUA - embed Lua in Mongoose (+100kb)
|
||||
|
||||
PROG = mongoose
|
||||
CFLAGS = -std=c99 -O2 -W -Wall -pedantic -pthread -pipe -I.. $(COPT)
|
||||
|
||||
# To build with Lua, download and unzip Lua 5.2.1 source code into the
|
||||
# mongoose directory, and then add $(LUA_SOURCES) to CFLAGS
|
||||
LUA = ../lua-5.2.1/src
|
||||
LUA_FLAGS = -DLUA_COMPAT_ALL
|
||||
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 \
|
||||
$(LUA)/lmem.c $(LUA)/lobject.c $(LUA)/lopcodes.c \
|
||||
$(LUA)/lparser.c $(LUA)/lstate.c $(LUA)/lstring.c \
|
||||
$(LUA)/ltable.c $(LUA)/ltm.c $(LUA)/lundump.c \
|
||||
$(LUA)/lvm.c $(LUA)/lzio.c $(LUA)/lauxlib.c \
|
||||
$(LUA)/lbaselib.c $(LUA)/lbitlib.c $(LUA)/lcorolib.c \
|
||||
$(LUA)/ldblib.c $(LUA)/liolib.c $(LUA)/lmathlib.c \
|
||||
$(LUA)/loslib.c $(LUA)/lstrlib.c $(LUA)/ltablib.c \
|
||||
$(LUA)/loadlib.c $(LUA)/linit.c
|
||||
LUA_HEADERS = $(LUA)/llimits.h $(LUA)/lobject.h $(LUA)/lapi.h $(LUA)/lzio.h \
|
||||
$(LUA)/ldo.h $(LUA)/ltm.h $(LUA)/lstate.h $(LUA)/lgc.h \
|
||||
$(LUA)/lvm.h $(LUA)/ltable.h $(LUA)/lstring.h $(LUA)/lfunc.h \
|
||||
$(LUA)/lundump.h $(LUA)/ldebug.h $(LUA)/lopcodes.h $(LUA)/llex.h \
|
||||
$(LUA)/lparser.h $(LUA)/lcode.h $(LUA)/lmem.h $(LUA)/lctype.h
|
||||
LUA_WINOBJS = $(LUA_SOURCES:%.c=%.obj)
|
||||
|
||||
ifneq ($(OS), Windows_NT)
|
||||
LUA_FLAGS += -DLUA_USE_DLOPEN
|
||||
endif
|
||||
|
||||
lua_5.2.1.c: lua_5.2.1.h
|
||||
(echo '#define LUA_CORE' ; \
|
||||
echo '#define LUA_LIB' ; \
|
||||
echo '#define lobject_c' ; \
|
||||
echo '#define lua_c' ; \
|
||||
echo '#define ltable_c' ; \
|
||||
echo '#include "lua_5.2.1.h"' ; \
|
||||
cat $(LUA_HEADERS) $(LUA_SOURCES) | sed '/^#include "/d') > $@
|
||||
|
||||
lua_5.2.1.h:
|
||||
cat $(LUA)/luaconf.h $(LUA)/lua.h $(LUA)/lauxlib.h $(LUA)/lualib.h | \
|
||||
sed '/^#include "/d' > $@
|
||||
|
||||
# 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
|
||||
|
||||
ALL_SOURCES = ../mongoose.c main.c sqlite3.c lsqlite3.c lua_5.2.1.c \
|
||||
$(YASSL_SOURCES)
|
||||
ALL_OBJECTS = $(ALL_SOURCES:%.c=%.o)
|
||||
ALL_WINOBJS = $(ALL_SOURCES:%.c=%.obj)
|
||||
|
||||
SQLITE_FLAGS = -DTHREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
|
||||
MONGOOSE_FLAGS = -DNO_SSL_DL -DUSE_LUA -DUSE_LUA_SQLITE3 -I.. $(COPT)
|
||||
|
||||
FLAGS = $(MONGOOSE_FLAGS) $(SQLITE_FLAGS) $(YASSL_FLAGS) $(LUA_FLAGS)
|
||||
|
||||
# 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) /W3 /GA /I$(MSVC)/include
|
||||
LINK_FLAGS = /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||
user32.lib shell32.lib comdlg32.lib ws2_32.lib advapi32.lib
|
||||
LINK = $(MSVC)/bin/link $(LINK_FLAGS)
|
||||
|
||||
all:
|
||||
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
|
||||
|
||||
%.obj: %.c
|
||||
$(CL) /c $(FLAGS) /Fo$@ $<
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -o $@ $< -c $(FLAGS) $(CFLAGS)
|
||||
|
||||
# Lua library for Windows
|
||||
lua.lib: $(LUA_WINOBJS)
|
||||
$(MSVC)/bin/lib /out:$@ $(LUA_WINOBJS)
|
||||
|
||||
# To build with lua, make sure you have Lua unpacked into lua-5.2.1 directory
|
||||
linux_lua: $(ALL_OBJECTS)
|
||||
$(CC) $(ALL_OBJECTS) -o $(PROG) -ldl
|
||||
|
||||
mongoose.o: mod_lua.c
|
||||
|
||||
# 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_lua: $(ALL_OBJECTS)
|
||||
$(CC) $(ALL_OBJECTS) -o $@
|
||||
|
||||
solaris:
|
||||
$(CC) ../mongoose.c main.c -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||
|
||||
lib$(PROG).a: $(ALL_OBJECTS)
|
||||
ar cr $@ $(ALL_OBJECTS)
|
||||
|
||||
$(PROG).lib: $(ALL_WINOBJS)
|
||||
$(MSVC)/bin/lib /out:$@ $(ALL_WINOBJS)
|
||||
|
||||
# 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
|
||||
Mongoose: ../mongoose.c main.c
|
||||
$(CC) $(ALL_SOURCES) \
|
||||
-DUSE_COCOA $(CFLAGS) $(FLAGS) -mmacosx-version-min=10.4 \
|
||||
-framework Cocoa -ObjC -arch i386 -arch x86_64 -o Mongoose
|
||||
|
||||
cocoa: 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 mongoose_*.png $$DIR/Contents/Resources/ && install -m 644 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
|
||||
|
||||
un:
|
||||
$(CC) ../test/unit_test.c -o unit_test lua_5.2.1.c $(CFLAGS) -g -O0
|
||||
./unit_test
|
||||
|
||||
wi:
|
||||
$(CL) test/unit_test.c $(LUA_SOURCES) $(LUA_FLAGS) \
|
||||
$(YASSL_SOURCES) $(YASSL_FLAGS) /I. /DNO_SSL_DL \
|
||||
/link /libpath:$(MSVC)/lib advapi32.lib /out:unit_test.exe
|
||||
./unit_test.exe
|
||||
|
||||
windows: $(ALL_WINOBJS)
|
||||
$(MSVC)/bin/rc res.rc
|
||||
$(LINK) /nologo $(ALL_WINOBJS) res.res /out:$(PROG).exe
|
||||
|
||||
windows_tiny: $(ALL_SOURCES)
|
||||
$(MSVC)/bin/rc res.rc
|
||||
$(CL) /nologo ../mongoose.c main.c /I.. \
|
||||
/link $(LINK_FLAGS) res.res /out:$(PROG)-tiny.exe
|
||||
|
||||
# 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)
|
||||
mingw:
|
||||
windres res.rc res.o
|
||||
$(CC) $(MINGWOPT) ../mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(CC) $(MINGWOPT) ../mongoose.c main.c res.o \
|
||||
-lws2_32 -ladvapi32 -lcomdlg32 -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 res.rc res.o
|
||||
$(CC) $(CYGWINOPT) ../mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(CC) $(CYGWINOPT) -I.. ../mongoose.c main.c res.o \
|
||||
-lws2_32 -ladvapi32 -o $(PROG).exe
|
||||
|
||||
tests:
|
||||
perl ../test/test.pl $(TEST)
|
||||
|
||||
tarball: clean
|
||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`.tgz ; cd ../.. && tar -czf x mongoose/{LICENSE,*.[ch]} && mv x mongoose/build/$$F
|
||||
|
||||
release: tarball cocoa
|
||||
wine make windows windows_tiny
|
||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' ../mongoose.c`; upx mongoose.exe; upx mongoose-tiny.exe ; cp mongoose.exe mongoose-$$V.exe; cp mongoose-tiny.exe mongoose-tiny-$$V.exe; cp mongoose.exe mongoose_php_bundle/; zip -r mongoose-php-$$V.zip mongoose_php_bundle/
|
||||
|
||||
clean:
|
||||
cd ../examples && $(MAKE) clean
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz \
|
||||
$(PROG).exe *.dll *.lib res.o res.RES *.dSYM *.zip *.pdb \
|
||||
*.exe *dmg* $(ALL_OBJECTS) $(ALL_WINOBJS)
|
||||
@@ -0,0 +1,6 @@
|
||||
LOCAL_PATH := $(call my-dir)/../..
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_CFLAGS := -std=c99 -O2 -W -Wall -pthread -pipe $(COPT)
|
||||
LOCAL_MODULE := mongoose
|
||||
LOCAL_SRC_FILES := main.c mongoose.c
|
||||
include $(BUILD_EXECUTABLE)
|
||||
+1883
File diff suppressed because it is too large.
Load diff
+17698
File diff suppressed because it is too large.
Load diff
+1247
File diff suppressed because it is too large.
Load diff
+160
-74
@@ -41,21 +41,30 @@
|
||||
#include <windows.h>
|
||||
#include <winsvc.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAX_PATH
|
||||
#endif
|
||||
|
||||
#ifndef S_ISDIR
|
||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||
#endif
|
||||
|
||||
#define DIRSEP '\\'
|
||||
#define snprintf _snprintf
|
||||
#define vsnprintf _vsnprintf
|
||||
#define sleep(x) Sleep((x) * 1000)
|
||||
#define WINCDECL __cdecl
|
||||
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#define DIRSEP '/'
|
||||
#define WINCDECL
|
||||
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
||||
#endif // _WIN32
|
||||
|
||||
#define MAX_OPTIONS 40
|
||||
#define MAX_OPTIONS 100
|
||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||
|
||||
static int exit_flag;
|
||||
@@ -68,7 +77,15 @@ static struct mg_context *ctx; // Set by start_mongoose()
|
||||
#endif /* !CONFIG_FILE */
|
||||
|
||||
static void WINCDECL signal_handler(int sig_num) {
|
||||
exit_flag = sig_num;
|
||||
#if !defined(_WIN32)
|
||||
// Do not do the trick with ignoring SIGCHLD, cause not all OSes (e.g. QNX)
|
||||
// reap zombies if SIGCHLD is ignored. On QNX, for example, waitpid()
|
||||
// fails if SIGCHLD is ignored, making system() non-functional.
|
||||
if (sig_num == SIGCHLD) {
|
||||
do {} while (waitpid(-1, &sig_num, WNOHANG) > 0);
|
||||
} else
|
||||
#endif
|
||||
{ exit_flag = sig_num; }
|
||||
}
|
||||
|
||||
static void die(const char *fmt, ...) {
|
||||
@@ -92,22 +109,19 @@ static void show_usage_and_exit(void) {
|
||||
const char **names;
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built %s\n",
|
||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka, built on %s\n",
|
||||
mg_version(), __DATE__);
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||
fprintf(stderr, " mongoose <config_file>\n");
|
||||
fprintf(stderr, " mongoose [config_file]\n");
|
||||
fprintf(stderr, " mongoose [-option value ...]\n");
|
||||
fprintf(stderr, "\nOPTIONS:\n");
|
||||
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 3) {
|
||||
fprintf(stderr, " -%s %s (default: \"%s\")\n",
|
||||
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
|
||||
for (i = 0; names[i] != NULL; i += 2) {
|
||||
fprintf(stderr, " -%s %s\n",
|
||||
names[i], names[i + 1] == NULL ? "<empty>" : names[i + 1]);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -149,32 +163,15 @@ static void create_config_file(const char *path) {
|
||||
} else if ((fp = fopen(path, "a+")) != NULL) {
|
||||
fprintf(fp, "%s", config_file_top_comment);
|
||||
names = mg_get_valid_option_names();
|
||||
for (i = 0; names[i] != NULL; i += 3) {
|
||||
value = mg_get_option(ctx, names[i]);
|
||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
||||
for (i = 0; names[i * 2] != NULL; i++) {
|
||||
value = mg_get_option(ctx, names[i * 2]);
|
||||
fprintf(fp, "# %s %s\n", names[i * 2], value ? value : "<value>");
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void verify_document_root(const char *root) {
|
||||
const char *p, *path;
|
||||
char buf[PATH_MAX];
|
||||
struct stat st;
|
||||
|
||||
path = root;
|
||||
if ((p = strchr(root, ',')) != NULL && (size_t) (p - root) < sizeof(buf)) {
|
||||
memcpy(buf, root, p - root);
|
||||
buf[p - root] = '\0';
|
||||
path = buf;
|
||||
}
|
||||
|
||||
if (stat(path, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
die("Invalid root directory: [%s]: %s", root, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static char *sdup(const char *str) {
|
||||
char *p;
|
||||
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
||||
@@ -186,16 +183,16 @@ static char *sdup(const char *str) {
|
||||
static void set_option(char **options, const char *name, const char *value) {
|
||||
int i;
|
||||
|
||||
if (!strcmp(name, "document_root") || !(strcmp(name, "r"))) {
|
||||
verify_document_root(value);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
||||
if (options[i] == NULL) {
|
||||
options[i] = sdup(name);
|
||||
options[i + 1] = sdup(value);
|
||||
options[i + 2] = NULL;
|
||||
break;
|
||||
} else if (!strcmp(options[i], name)) {
|
||||
free(options[i + 1]);
|
||||
options[i + 1] = sdup(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,8 +206,6 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
||||
FILE *fp = NULL;
|
||||
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
||||
|
||||
options[0] = NULL;
|
||||
|
||||
// Should we use a config file ?
|
||||
if (argv[1] != NULL && argv[1][0] != '-') {
|
||||
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
||||
@@ -255,31 +250,97 @@ 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]);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_server_name(void) {
|
||||
snprintf(server_name, sizeof(server_name), "Mongoose web server v. %s",
|
||||
snprintf(server_name, sizeof(server_name), "Mongoose web server v.%s",
|
||||
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);
|
||||
}
|
||||
static int log_message(const struct mg_connection *conn, const char *message) {
|
||||
(void) conn;
|
||||
printf("%s\n", message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int is_path_absolute(const char *path) {
|
||||
#ifdef _WIN32
|
||||
return path != NULL &&
|
||||
((path[0] == '\\' && path[1] == '\\') || // UNC path, e.g. \\server\dir
|
||||
(isalpha(path[0]) && path[1] == ':' && path[2] == '\\')); // E.g. X:\dir
|
||||
#else
|
||||
return path != NULL && path[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
static char *get_option(char **options, const char *option_name) {
|
||||
int i;
|
||||
|
||||
for (i = 0; options[i] != NULL; i++)
|
||||
if (!strcmp(options[i], option_name))
|
||||
return options[i + 1];
|
||||
|
||||
// Returning NULL marks request as not handled, signalling mongoose to
|
||||
// proceed with handling it.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void verify_existence(char **options, const char *option_name,
|
||||
int must_be_dir) {
|
||||
struct stat st;
|
||||
const char *path = get_option(options, option_name);
|
||||
|
||||
if (path != NULL && (stat(path, &st) != 0 ||
|
||||
((S_ISDIR(st.st_mode) ? 1 : 0) != must_be_dir))) {
|
||||
die("Invalid path for %s: [%s]: (%s). Make sure that path is either "
|
||||
"absolute, or it is relative to mongoose executable.",
|
||||
option_name, path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void set_absolute_path(char *options[], const char *option_name,
|
||||
const char *path_to_mongoose_exe) {
|
||||
char path[PATH_MAX], abs[PATH_MAX], *option_value;
|
||||
const char *p;
|
||||
|
||||
// Check whether option is already set
|
||||
option_value = get_option(options, option_name);
|
||||
|
||||
// If option is already set and it is an absolute path,
|
||||
// leave it as it is -- it's already absolute.
|
||||
if (option_value != NULL && !is_path_absolute(option_value)) {
|
||||
// Not absolute. Use the directory where mongoose executable lives
|
||||
// be the relative directory for everything.
|
||||
// Extract mongoose executable directory into path.
|
||||
if ((p = strrchr(path_to_mongoose_exe, DIRSEP)) == NULL) {
|
||||
getcwd(path, sizeof(path));
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "%.*s", (int) (p - path_to_mongoose_exe),
|
||||
path_to_mongoose_exe);
|
||||
}
|
||||
|
||||
strncat(path, "/", sizeof(path) - 1);
|
||||
strncat(path, option_value, sizeof(path) - 1);
|
||||
|
||||
// Absolutize the path, and set the option
|
||||
abs_path(path, abs, sizeof(abs));
|
||||
set_option(options, option_name, abs);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_mongoose(int argc, char *argv[]) {
|
||||
struct mg_callbacks callbacks;
|
||||
char *options[MAX_OPTIONS];
|
||||
int i;
|
||||
|
||||
@@ -297,15 +358,35 @@ static void start_mongoose(int argc, char *argv[]) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
|
||||
/* Update config based on command line arguments */
|
||||
options[0] = NULL;
|
||||
set_option(options, "document_root", ".");
|
||||
|
||||
// Update config based on command line arguments
|
||||
process_command_line_arguments(argv, options);
|
||||
|
||||
/* Setup signal handler: quit on Ctrl-C */
|
||||
// Make sure we have absolute paths for files and directories
|
||||
// https://github.com/valenok/mongoose/issues/181
|
||||
set_absolute_path(options, "document_root", argv[0]);
|
||||
set_absolute_path(options, "put_delete_auth_file", argv[0]);
|
||||
set_absolute_path(options, "cgi_interpreter", argv[0]);
|
||||
set_absolute_path(options, "access_log_file", argv[0]);
|
||||
set_absolute_path(options, "error_log_file", argv[0]);
|
||||
set_absolute_path(options, "global_auth_file", argv[0]);
|
||||
set_absolute_path(options, "ssl_certificate", argv[0]);
|
||||
|
||||
// Make extra verification for certain options
|
||||
verify_existence(options, "document_root", 1);
|
||||
verify_existence(options, "cgi_interpreter", 0);
|
||||
verify_existence(options, "ssl_certificate", 0);
|
||||
|
||||
// Setup signal handler: quit on Ctrl-C
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
/* Start Mongoose */
|
||||
ctx = mg_start(&mongoose_callback, NULL, (const char **) options);
|
||||
// Start Mongoose
|
||||
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]);
|
||||
}
|
||||
@@ -408,16 +489,16 @@ static void save_config(HWND hDlg, FILE *fp) {
|
||||
|
||||
fprintf(fp, "%s", config_file_top_comment);
|
||||
options = mg_get_valid_option_names();
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
id = ID_CONTROLS + i / 3;
|
||||
for (i = 0; options[i * 2] != NULL; i++) {
|
||||
name = options[i * 2];
|
||||
id = ID_CONTROLS + i;
|
||||
if (is_boolean_option(name)) {
|
||||
snprintf(value, sizeof(value), "%s",
|
||||
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
||||
} else {
|
||||
GetDlgItemText(hDlg, id, value, sizeof(value));
|
||||
}
|
||||
default_value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||
default_value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
|
||||
// If value is the same as default, skip it
|
||||
if (strcmp(value, default_value) != 0) {
|
||||
fprintf(fp, "%s %s\n", name, value);
|
||||
@@ -448,23 +529,23 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
||||
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
||||
break;
|
||||
case ID_RESET_DEFAULTS:
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||
for (i = 0; options[i * 2] != NULL; i++) {
|
||||
name = options[i * 2];
|
||||
value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
|
||||
if (is_boolean_option(name)) {
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
|
||||
BST_CHECKED : BST_UNCHECKED);
|
||||
} else {
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
for (i = 0; options[i * 2] != NULL; i++) {
|
||||
name = options[i * 2];
|
||||
if ((is_filename_option(name) || is_directory_option(name)) &&
|
||||
LOWORD(wParam) == ID_CONTROLS + i / 3 + ID_FILE_BUTTONS_DELTA) {
|
||||
LOWORD(wParam) == ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA) {
|
||||
OPENFILENAME of;
|
||||
BROWSEINFO bi;
|
||||
char path[PATH_MAX] = "";
|
||||
@@ -489,7 +570,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
||||
}
|
||||
|
||||
if (path[0] != '\0') {
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), path);
|
||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), path);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -501,14 +582,14 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
||||
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
||||
SetWindowText(hDlg, "Mongoose settings");
|
||||
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
||||
for (i = 0; options[i] != NULL; i += 3) {
|
||||
name = options[i + 1];
|
||||
for (i = 0; options[i * 2] != NULL; i++) {
|
||||
name = options[i * 2];
|
||||
value = mg_get_option(ctx, name);
|
||||
if (is_boolean_option(name)) {
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||
CheckDlgButton(hDlg, ID_CONTROLS + i, !strcmp(value, "yes") ?
|
||||
BST_CHECKED : BST_UNCHECKED);
|
||||
} else {
|
||||
SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
||||
SetDlgItemText(hDlg, ID_CONTROLS + i, value == NULL ? "" : value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -585,8 +666,8 @@ static void show_settings_dialog() {
|
||||
p = mem + sizeof(dialog_header);
|
||||
|
||||
option_names = mg_get_valid_option_names();
|
||||
for (i = 0; option_names[i] != NULL; i += 3) {
|
||||
long_option_name = option_names[i + 1];
|
||||
for (i = 0; option_names[i * 2] != NULL; i++) {
|
||||
long_option_name = option_names[i * 2];
|
||||
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
||||
x = 10 + (WIDTH / 2) * (nelems % 2);
|
||||
y = (nelems/2 + 1) * HEIGHT + 5;
|
||||
@@ -604,7 +685,7 @@ static void show_settings_dialog() {
|
||||
width -= 20;
|
||||
cl = 0x81;
|
||||
add_control(&p, dia, 0x80,
|
||||
ID_CONTROLS + (i / 3) + ID_FILE_BUTTONS_DELTA,
|
||||
ID_CONTROLS + i + ID_FILE_BUTTONS_DELTA,
|
||||
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
||||
(WORD) (x + width + LABEL_WIDTH + 5),
|
||||
y, 15, 12, "...");
|
||||
@@ -614,7 +695,7 @@ static void show_settings_dialog() {
|
||||
}
|
||||
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
||||
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
||||
add_control(&p, dia, cl, ID_CONTROLS + (i / 3), style,
|
||||
add_control(&p, dia, cl, ID_CONTROLS + i, style,
|
||||
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
||||
nelems++;
|
||||
}
|
||||
@@ -688,6 +769,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
char buf[200], *service_argv[] = {__argv[0], NULL};
|
||||
POINT pt;
|
||||
HMENU hMenu;
|
||||
static UINT s_uTaskbarRestart; // for taskbar creation
|
||||
|
||||
switch (msg) {
|
||||
case WM_CREATE:
|
||||
@@ -698,6 +780,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
start_mongoose(__argc, __argv);
|
||||
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||
}
|
||||
break;
|
||||
case WM_COMMAND:
|
||||
@@ -755,6 +838,9 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||
PostQuitMessage(0);
|
||||
return 0; // We've just sent our own quit message, with proper hwnd.
|
||||
default:
|
||||
if (msg==s_uTaskbarRestart)
|
||||
Shell_NotifyIcon(NIM_ADD, &TrayIcon);
|
||||
}
|
||||
|
||||
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||
@@ -807,7 +893,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);
|
||||
+393
@@ -0,0 +1,393 @@
|
||||
#include "lua_5.2.1.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
|
||||
int offset) {
|
||||
HANDLE fh = (HANDLE) _get_osfhandle(fd);
|
||||
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
|
||||
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
|
||||
CloseHandle(mh);
|
||||
return p;
|
||||
}
|
||||
#define munmap(x, y) UnmapViewOfFile(x)
|
||||
#define MAP_FAILED NULL
|
||||
#define MAP_PRIVATE 0
|
||||
#define PROT_READ 0
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
static const char *LUASOCKET = "luasocket";
|
||||
|
||||
// Forward declarations
|
||||
static void handle_request(struct mg_connection *);
|
||||
static int handle_lsp_request(struct mg_connection *, const char *,
|
||||
struct file *, struct lua_State *);
|
||||
|
||||
static void reg_string(struct lua_State *L, const char *name, const char *val) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushstring(L, val);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static void reg_int(struct lua_State *L, const char *name, int val) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushinteger(L, val);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static void reg_function(struct lua_State *L, const char *name,
|
||||
lua_CFunction func, struct mg_connection *conn) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, func, 1);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static int lsp_sock_close(lua_State *L) {
|
||||
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "sock");
|
||||
closesocket((SOCKET) lua_tonumber(L, -1));
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_sock_recv(lua_State *L) {
|
||||
char buf[2000];
|
||||
int n;
|
||||
|
||||
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "sock");
|
||||
n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
|
||||
if (n <= 0) {
|
||||
lua_pushnil(L);
|
||||
} else {
|
||||
lua_pushlstring(L, buf, n);
|
||||
}
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_sock_send(lua_State *L) {
|
||||
const char *buf;
|
||||
size_t len, sent = 0;
|
||||
int n, sock;
|
||||
|
||||
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
|
||||
buf = lua_tolstring(L, -1, &len);
|
||||
lua_getfield(L, -2, "sock");
|
||||
sock = (int) lua_tonumber(L, -1);
|
||||
while (sent < len) {
|
||||
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
|
||||
break;
|
||||
}
|
||||
sent += n;
|
||||
}
|
||||
lua_pushnumber(L, n);
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct luaL_Reg luasocket_methods[] = {
|
||||
{"close", lsp_sock_close},
|
||||
{"send", lsp_sock_send},
|
||||
{"recv", lsp_sock_recv},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static int lsp_connect(lua_State *L) {
|
||||
char ebuf[100];
|
||||
SOCKET sock;
|
||||
|
||||
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
|
||||
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
|
||||
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
|
||||
if (sock == INVALID_SOCKET) {
|
||||
return luaL_error(L, ebuf);
|
||||
} else {
|
||||
lua_newtable(L);
|
||||
reg_int(L, "sock", sock);
|
||||
reg_string(L, "host", lua_tostring(L, -4));
|
||||
luaL_getmetatable(L, LUASOCKET);
|
||||
lua_setmetatable(L, -2);
|
||||
}
|
||||
} else {
|
||||
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_error(lua_State *L) {
|
||||
lua_getglobal(L, "mg");
|
||||
lua_getfield(L, -1, "onerror");
|
||||
lua_pushvalue(L, -3);
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Silently stop processing chunks.
|
||||
static void lsp_abort(lua_State *L) {
|
||||
int top = lua_gettop(L);
|
||||
lua_getglobal(L, "mg");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "onerror");
|
||||
lua_settop(L, top);
|
||||
lua_pushstring(L, "aborting");
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
static int lsp(struct mg_connection *conn, const char *path,
|
||||
const char *p, int64_t len, lua_State *L) {
|
||||
int i, j, pos = 0, lines = 1, lualines = 0;
|
||||
char chunkname[MG_BUF_LEN];
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] == '\n') lines++;
|
||||
if (p[i] == '<' && p[i + 1] == '?') {
|
||||
for (j = i + 1; j < len ; j++) {
|
||||
if (p[j] == '\n') lualines++;
|
||||
if (p[j] == '?' && p[j + 1] == '>') {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
|
||||
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, lsp_error, 1);
|
||||
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
|
||||
// Syntax error or OOM. Error message is pushed on stack.
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
} else {
|
||||
// Success loading chunk. Call it.
|
||||
lua_pcall(L, 0, 0, 1);
|
||||
}
|
||||
|
||||
pos = j + 2;
|
||||
i = pos - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lualines > 0) {
|
||||
lines += lualines;
|
||||
lualines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i > pos) {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsp_write(lua_State *L) {
|
||||
int i, num_args;
|
||||
const char *str;
|
||||
size_t size;
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
num_args = lua_gettop(L);
|
||||
for (i = 1; i <= num_args; i++) {
|
||||
if (lua_isstring(L, i)) {
|
||||
str = lua_tolstring(L, i, &size);
|
||||
mg_write(conn, str, size);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsp_read(lua_State *L) {
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
char buf[1024];
|
||||
int len = mg_read(conn, buf, sizeof(buf));
|
||||
|
||||
if (len <= 0) return 0;
|
||||
lua_pushlstring(L, buf, len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// mg.include: Include another .lp file
|
||||
static int lsp_include(lua_State *L) {
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
|
||||
// handle_lsp_request returned an error code, meaning an error occured in
|
||||
// the included page and mg.onerror returned non-zero. Stop processing.
|
||||
lsp_abort(L);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// mg.cry: Log an error. Default value for mg.onerror.
|
||||
static int lsp_cry(lua_State *L){
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
cry(conn, "%s", lua_tostring(L, -1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// mg.redirect: Redirect the request (internally).
|
||||
static int lsp_redirect(lua_State *L) {
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
conn->request_info.uri = lua_tostring(L, -1);
|
||||
handle_request(conn);
|
||||
lsp_abort(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) {
|
||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
extern void luaL_openlibs(lua_State *);
|
||||
int i;
|
||||
|
||||
luaL_openlibs(L);
|
||||
#ifdef USE_LUA_SQLITE3
|
||||
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
|
||||
#endif
|
||||
|
||||
luaL_newmetatable(L, LUASOCKET);
|
||||
lua_pushliteral(L, "__index");
|
||||
luaL_newlib(L, luasocket_methods);
|
||||
lua_rawset(L, -3);
|
||||
lua_pop(L, 1);
|
||||
lua_register(L, "connect", lsp_connect);
|
||||
|
||||
if (conn == NULL) return;
|
||||
|
||||
// Register mg module
|
||||
lua_newtable(L);
|
||||
|
||||
reg_function(L, "read", lsp_read, conn);
|
||||
reg_function(L, "write", lsp_write, conn);
|
||||
reg_function(L, "cry", lsp_cry, conn);
|
||||
reg_function(L, "include", lsp_include, conn);
|
||||
reg_function(L, "redirect", lsp_redirect, conn);
|
||||
reg_string(L, "version", MONGOOSE_VERSION);
|
||||
|
||||
// Export request_info
|
||||
lua_pushstring(L, "request_info");
|
||||
lua_newtable(L);
|
||||
reg_string(L, "request_method", ri->request_method);
|
||||
reg_string(L, "uri", ri->uri);
|
||||
reg_string(L, "http_version", ri->http_version);
|
||||
reg_string(L, "query_string", ri->query_string);
|
||||
reg_int(L, "remote_ip", ri->remote_ip);
|
||||
reg_int(L, "remote_port", ri->remote_port);
|
||||
reg_int(L, "num_headers", ri->num_headers);
|
||||
lua_pushstring(L, "http_headers");
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < ri->num_headers; i++) {
|
||||
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
|
||||
}
|
||||
lua_rawset(L, -3);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
lua_setglobal(L, "mg");
|
||||
|
||||
// Register default mg.onerror function
|
||||
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
|
||||
"debug.traceback(e, 1)) end");
|
||||
}
|
||||
|
||||
static int lua_error_handler(lua_State *L) {
|
||||
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
|
||||
|
||||
lua_getglobal(L, "mg");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_getfield(L, -1, "write"); // call mg.write()
|
||||
lua_pushstring(L, error_msg);
|
||||
lua_pushliteral(L, "\n");
|
||||
lua_call(L, 2, 0);
|
||||
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
|
||||
} else {
|
||||
printf("Lua error: [%s]\n", error_msg);
|
||||
luaL_dostring(L, "print(debug.traceback(), '\\n')");
|
||||
}
|
||||
// TODO(lsm): leave the stack balanced
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mg_exec_lua_script(struct mg_connection *conn, const char *path,
|
||||
const void **exports) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
|
||||
if (path != NULL && (L = luaL_newstate()) != NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
lua_pushcclosure(L, &lua_error_handler, 0);
|
||||
|
||||
lua_pushglobaltable(L);
|
||||
if (exports != NULL) {
|
||||
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
|
||||
lua_pushstring(L, exports[i]);
|
||||
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
}
|
||||
|
||||
if (luaL_loadfile(L, path) != 0) {
|
||||
lua_error_handler(L);
|
||||
}
|
||||
lua_pcall(L, 0, 0, -2);
|
||||
lua_close(L);
|
||||
}
|
||||
}
|
||||
|
||||
static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
|
||||
const char *fmt, ...) {
|
||||
char buf[MG_BUF_LEN];
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (L == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", buf);
|
||||
} else {
|
||||
lua_pushstring(L, buf);
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_lsp_request(struct mg_connection *conn, const char *path,
|
||||
struct file *filep, struct lua_State *ls) {
|
||||
void *p = NULL;
|
||||
lua_State *L = NULL;
|
||||
int error = 1;
|
||||
|
||||
// We need both mg_stat to get file size, and mg_fopen to get fd
|
||||
if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
|
||||
lsp_send_err(conn, ls, "File [%s] not found", path);
|
||||
} else if (filep->membuf == NULL &&
|
||||
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
|
||||
fileno(filep->fp), 0)) == MAP_FAILED) {
|
||||
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
|
||||
fileno(filep->fp), strerror(errno));
|
||||
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
|
||||
} else {
|
||||
// We're not sending HTTP headers here, Lua page must do it.
|
||||
if (ls == NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
if (conn->ctx->callbacks.init_lua != NULL) {
|
||||
conn->ctx->callbacks.init_lua(conn, L);
|
||||
}
|
||||
}
|
||||
error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
|
||||
filep->size, L);
|
||||
}
|
||||
|
||||
if (L != NULL && ls == NULL) lua_close(L);
|
||||
if (p != NULL) munmap(p, filep->size);
|
||||
mg_fclose(filep);
|
||||
return error;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 669 B |
Binary file not shown.
|
After Width: | Height: | Size: 872 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
+140456
File diff suppressed because it is too large.
Load diff
+7245
File diff suppressed because it is too large.
Load diff
+47
-19
@@ -1,28 +1,56 @@
|
||||
CFLAGS= -W -Wall -I.. -pthread -g
|
||||
CFLAGS = -W -Wall -I.. -pthread -g -pipe $(COPT)
|
||||
DLL_FLAGS = -DLUA_COMPAT_ALL -I../build
|
||||
|
||||
all:
|
||||
OS=`uname`; \
|
||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
||||
$(CC) $(CFLAGS) upload.c ../mongoose.c $$LIBS $(ADD) -o upload;
|
||||
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
||||
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
||||
ifeq ($(OS),Windows_NT)
|
||||
else
|
||||
UNAME_S := $(shell uname -s)
|
||||
DLL_FLAGS += -shared
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
CFLAGS += -ldl
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
# DLL_FLAGS += -bundle -undefined dynamic_lookup -dynamiclib
|
||||
DLL_FLAGS += -flat_namespace -undefined suppress -dynamiclib
|
||||
endif
|
||||
endif
|
||||
|
||||
all: hello upload post websocket chat
|
||||
|
||||
hello:
|
||||
$(CC) hello.c ../mongoose.c -o $@ $(CFLAGS)
|
||||
|
||||
upload:
|
||||
$(CC) upload.c ../mongoose.c -o $@ $(CFLAGS)
|
||||
|
||||
post:
|
||||
$(CC) post.c ../mongoose.c -o $@ $(CFLAGS)
|
||||
|
||||
websocket:
|
||||
$(CC) -DUSE_WEBSOCKET websocket.c ../mongoose.c -o $@ $(CFLAGS)
|
||||
|
||||
chat:
|
||||
$(CC) chat.c ../mongoose.c -o $@ $(CFLAGS)
|
||||
|
||||
lua_dll:
|
||||
$(CC) lua_dll.c ../build/lua_5.2.1.c -o $@.so $(CFLAGS) $(DLL_FLAGS)
|
||||
|
||||
|
||||
MSVC = e:/vc6
|
||||
MSVC = ../../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
|
||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
|
||||
LFLAGS = /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)
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||
#$(CL) lua_dll.c $(CLFLAGS) $(DLL_FLAGS) /DLL $(LFLAGS) /SUBSYSTEM:WINDOWS /ENTRY:luaopen_lua_dll /EXPORT:luaopen_lua_dll /out:lua_dll.dll
|
||||
|
||||
clean:
|
||||
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj
|
||||
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj .*o
|
||||
+21
-26
@@ -56,9 +56,10 @@ static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
// Get session object for the connection. Caller must hold the lock.
|
||||
static struct session *get_session(const struct mg_connection *conn) {
|
||||
int i;
|
||||
const char *cookie = mg_get_header(conn, "Cookie");
|
||||
char session_id[33];
|
||||
time_t now = time(NULL);
|
||||
mg_get_cookie(conn, "session", session_id, sizeof(session_id));
|
||||
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire != 0 &&
|
||||
sessions[i].expire > now &&
|
||||
@@ -325,34 +326,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 +357,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 +365,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
@@ -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;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
<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 mg.write('<span>' .. i .. '</span> ') 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: [<?
|
||||
local post_data = ''
|
||||
if mg.request_info.request_method == 'POST' then
|
||||
post_data = mg.read()
|
||||
end
|
||||
mg.write(post_data)
|
||||
?>]
|
||||
request method: [<? mg.write(mg.request_info.request_method) ?>]
|
||||
IP/port: [<? mg.write(mg.request_info.remote_ip, ':',
|
||||
mg.request_info.remote_port) ?>]
|
||||
URI: [<? mg.write(mg.request_info.uri) ?>]
|
||||
HTTP version [<? mg.write(mg.request_info.http_version) ?>]
|
||||
HEADERS:
|
||||
<?
|
||||
for name, value in pairs(mg.request_info.http_headers) do
|
||||
mg.write(name, ':', value, '\n')
|
||||
end
|
||||
?>
|
||||
</pre>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
#include "lua-5.2.1.h"
|
||||
|
||||
static int smile(lua_State *L) {
|
||||
(void) L; // Unused
|
||||
printf("%s\n", ":-)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUA_API luaopen_lua_dll(lua_State *L) {
|
||||
static const struct luaL_Reg api[] = {
|
||||
{"smile", smile},
|
||||
{NULL, NULL},
|
||||
};
|
||||
luaL_openlib(L, "lua_dll", api, 0);
|
||||
return 1;
|
||||
}
|
||||
+27
-32
@@ -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
@@ -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;
|
||||
|
||||
+20
-51
@@ -5,69 +5,38 @@
|
||||
#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) {
|
||||
static const char *message = "server ready";
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
|
||||
}
|
||||
|
||||
// Read message from the client.
|
||||
// Accept only small (<126 bytes) messages.
|
||||
len = 0;
|
||||
msg_len = mask_len = 0;
|
||||
for (;;) {
|
||||
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
|
||||
return ""; // Read error, close websocket
|
||||
}
|
||||
len += n;
|
||||
if (len >= 2) {
|
||||
msg_len = buf[1] & 127;
|
||||
mask_len = (buf[1] & 128) ? 4 : 0;
|
||||
if (msg_len > 125) {
|
||||
return ""; // Message is too long, close websocket
|
||||
}
|
||||
// If we've buffered the whole message, exit the loop
|
||||
if (len >= 2 + mask_len + msg_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Arguments:
|
||||
// flags: first byte of websocket frame, see websocket RFC,
|
||||
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||
// data, data_len: payload data. Mask, if any, is already applied.
|
||||
static int websocket_data_handler(struct mg_connection *conn, int flags,
|
||||
char *data, size_t data_len) {
|
||||
(void) flags; // Unused
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||
|
||||
// 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;
|
||||
}
|
||||
// Returning zero means stoping websocket conversation.
|
||||
// Close the conversation if client has sent us "exit" string.
|
||||
return memcmp(data, "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);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebSocket Test</title>
|
||||
<script language="javascript" type="text/javascript">
|
||||
|
||||
var writeToScreen = function(message) {
|
||||
@@ -29,9 +29,9 @@
|
||||
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
||||
};
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
<style> div {font: small Verdana; } </style>
|
||||
<h2>Mongoose WebSocket Test</h2>
|
||||
<h2>Mongoose WebSocket Test</h2>
|
||||
|
||||
<div style="width: 400px; color: #aaa; padding: 1em; ">
|
||||
This page code creates websocket to the URI "/foo",
|
||||
@@ -40,5 +40,5 @@
|
||||
receiving the "exit" message.
|
||||
</div>
|
||||
|
||||
<div id="output"></div>
|
||||
<div id="output"></div>
|
||||
</html>
|
||||
-184
@@ -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 .
|
||||
+1138
-866
File diff suppressed because it is too large.
Load diff
+166
-148
@@ -1,22 +1,19 @@
|
||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
||||
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
|
||||
// Copyright (c) 2013 Cesanta Software Limited
|
||||
// All rights reserved
|
||||
//
|
||||
// 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:
|
||||
// This library is dual-licensed: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License version 2 as
|
||||
// published by the Free Software Foundation. For the terms of this
|
||||
// license, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
// You are free to use this library under the terms of the GNU General
|
||||
// Public License, but WITHOUT ANY WARRANTY; without even the implied
|
||||
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
// See the GNU General Public License for more details.
|
||||
//
|
||||
// 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.
|
||||
// Alternatively, you can license this library under a commercial
|
||||
// license, as set out in <http://cesanta.com/products.html>.
|
||||
|
||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||
#define MONGOOSE_HEADER_INCLUDED
|
||||
@@ -42,129 +39,115 @@ 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()
|
||||
void *conn_data; // Connection-specific, per-thread user data.
|
||||
|
||||
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,
|
||||
// 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 {
|
||||
// Called when mongoose has received new HTTP request.
|
||||
// If callback returns non-zero,
|
||||
// callback must process the request by sending valid HTTP headers and body,
|
||||
// and mongoose will not do any further processing.
|
||||
// If callback returns 0, mongoose processes the request itself. In this case,
|
||||
// callback must not send any data to the client.
|
||||
int (*begin_request)(struct mg_connection *);
|
||||
|
||||
// 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,
|
||||
// Called when mongoose has finished processing request.
|
||||
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||
|
||||
// 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,
|
||||
// Called when mongoose is about to log a message. If callback returns
|
||||
// non-zero, mongoose does not log anything.
|
||||
int (*log_message)(const struct mg_connection *, const char *message);
|
||||
|
||||
// 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,
|
||||
// Called when mongoose initializes SSL library.
|
||||
int (*init_ssl)(void *ssl_context, void *user_data);
|
||||
|
||||
// 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,
|
||||
// Called when websocket request is received, before websocket handshake.
|
||||
// If callback returns 0, mongoose proceeds with handshake, otherwise
|
||||
// cinnection is closed immediately.
|
||||
int (*websocket_connect)(const struct mg_connection *);
|
||||
|
||||
// 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,
|
||||
// Called when websocket handshake is successfully completed, and
|
||||
// connection is ready for data exchange.
|
||||
void (*websocket_ready)(struct mg_connection *);
|
||||
|
||||
// Handshake has been successfully completed.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_READY,
|
||||
// Called when data frame has been received from the client.
|
||||
// Parameters:
|
||||
// bits: first byte of the websocket frame, see websocket RFC at
|
||||
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||
// data, data_len: payload, with mask (if any) already applied.
|
||||
// Return value:
|
||||
// non-0: keep this websocket connection opened.
|
||||
// 0: close this websocket connection.
|
||||
int (*websocket_data)(struct mg_connection *, int bits,
|
||||
char *data, size_t data_len);
|
||||
|
||||
// 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,
|
||||
// Called when mongoose tries to open a file. Used to intercept file open
|
||||
// calls, and serve file data from memory instead.
|
||||
// Parameters:
|
||||
// path: Full path to the file to open.
|
||||
// data_len: Placeholder for the file size, if file is served from memory.
|
||||
// Return value:
|
||||
// NULL: do not serve file from memory, proceed with normal file open.
|
||||
// non-NULL: pointer to the file contents in memory. data_len must be
|
||||
// initilized with the size of the memory block.
|
||||
const char * (*open_file)(const struct mg_connection *,
|
||||
const char *path, size_t *data_len);
|
||||
|
||||
// Client has closed the connection.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_CLOSE,
|
||||
// Called when mongoose is about to serve Lua server page (.lp file), if
|
||||
// Lua support is enabled.
|
||||
// Parameters:
|
||||
// lua_context: "lua_State *" pointer.
|
||||
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||
|
||||
// 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,
|
||||
// Called when mongoose has uploaded a file to a temporary directory as a
|
||||
// result of mg_upload() call.
|
||||
// Parameters:
|
||||
// file_file: full path name to the uploaded file.
|
||||
void (*upload)(struct mg_connection *, const char *file_name);
|
||||
|
||||
// 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,
|
||||
// Called at the beginning of mongoose's thread execution in the context of
|
||||
// that thread. To be used to perform any extra per-thread initialization.
|
||||
// Parameters:
|
||||
// user_data: pointer passed to mg_start
|
||||
// conn_data: per-connection, i.e. per-thread pointer. Can be used to
|
||||
// store per-thread data, for example, database connection
|
||||
// handles. Persistent between connections handled by the
|
||||
// same thread.
|
||||
// NOTE: this parameter is NULL for master thread, and non-NULL
|
||||
// for worker threads.
|
||||
void (*thread_start)(void *user_data, void **conn_data);
|
||||
|
||||
// Mongoose has uploaded file to a temporary directory.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NUL-terminated file name.
|
||||
MG_UPLOAD,
|
||||
// Called when mongoose's thread is about to terminate.
|
||||
// Same as thread_start() callback, but called when thread is about to be
|
||||
// destroyed. Used to cleanup the state initialized by thread_start().
|
||||
// Parameters: see thread_start().
|
||||
void (*thread_stop)(void *user_data, void **conn_data);
|
||||
};
|
||||
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
|
||||
// processing is required for these, signal handlers must be set up
|
||||
// Side-effects: on UNIX, ignores SIGPIPE signals. If custom
|
||||
// processing is required SIGPIPE, signal handler must be set up
|
||||
// after calling mg_start().
|
||||
//
|
||||
// Important: Mongoose does not install SIGCHLD handler. If CGI is used,
|
||||
// SIGCHLD handler must be set up to reap CGI zombie processes.
|
||||
//
|
||||
// Example:
|
||||
// const char *options[] = {
|
||||
@@ -174,13 +157,14 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
|
||||
// };
|
||||
// struct mg_context *ctx = mg_start(&my_func, NULL, options);
|
||||
//
|
||||
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
|
||||
// Refer to https://github.com/valenok/mongoose/blob/master/UserManual.md
|
||||
// for the list of valid option and their possible values.
|
||||
//
|
||||
// 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.
|
||||
@@ -201,7 +185,8 @@ const char *mg_get_option(const struct mg_context *ctx, const char *name);
|
||||
|
||||
|
||||
// Return array of strings that represent valid configuration options.
|
||||
// For each option, a short name, long name, and default value is returned.
|
||||
// For each option, option name and default value is returned, i.e. the
|
||||
// number of entries in the array equals to number_of_options x 2.
|
||||
// Array is NULL terminated.
|
||||
const char **mg_get_valid_option_names(void);
|
||||
|
||||
@@ -232,20 +217,37 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
||||
// Return:
|
||||
// 0 when the connection has been closed
|
||||
// -1 on error
|
||||
// number of bytes written on success
|
||||
// >0 number of bytes written on success
|
||||
int mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||
|
||||
|
||||
// Send data to the browser using printf() semantics.
|
||||
// Send data to a websocket client wrapped in a websocket frame.
|
||||
// It is unsafe to read/write to this connection from another thread.
|
||||
// This function is available when mongoose is compiled with -DUSE_WEBSOCKET
|
||||
//
|
||||
// Works exactly like mg_write(), but allows to do message formatting.
|
||||
// Below are the macros for enabling compiler-specific checks for
|
||||
// printf-like arguments.
|
||||
// Return:
|
||||
// 0 when the connection has been closed
|
||||
// -1 on error
|
||||
// >0 number of bytes written on success
|
||||
int mg_websocket_write(struct mg_connection* conn, int opcode,
|
||||
const char *data, size_t data_len);
|
||||
|
||||
// Opcodes, from http://tools.ietf.org/html/rfc6455
|
||||
enum {
|
||||
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||
WEBSOCKET_OPCODE_PING = 0x9,
|
||||
WEBSOCKET_OPCODE_PONG = 0xa
|
||||
};
|
||||
|
||||
|
||||
// Macros for enabling compiler-specific checks for printf-like arguments.
|
||||
#undef PRINTF_FORMAT_STRING
|
||||
#if _MSC_VER >= 1400
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||
#include <sal.h>
|
||||
#if _MSC_VER > 1400
|
||||
#if defined(_MSC_VER) && _MSC_VER > 1400
|
||||
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
||||
#else
|
||||
#define PRINTF_FORMAT_STRING(s) __format_string s
|
||||
@@ -260,6 +262,9 @@ int mg_write(struct mg_connection *, const void *buf, size_t len);
|
||||
#define PRINTF_ARGS(x, y)
|
||||
#endif
|
||||
|
||||
// Send data to the client using printf() semantics.
|
||||
//
|
||||
// Works exactly like mg_write(), but allows to do message formatting.
|
||||
int mg_printf(struct mg_connection *,
|
||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||
|
||||
@@ -269,6 +274,10 @@ void mg_send_file(struct mg_connection *conn, const char *path);
|
||||
|
||||
|
||||
// Read data from the remote end, return number of bytes read.
|
||||
// Return:
|
||||
// 0 connection has been closed by peer. No more data could be read.
|
||||
// < 0 read error. No more data could be read from the connection.
|
||||
// > 0 number of bytes read into the buffer.
|
||||
int mg_read(struct mg_connection *, void *buf, size_t len);
|
||||
|
||||
|
||||
@@ -294,7 +303,8 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
// On success, length of the decoded variable.
|
||||
// On error:
|
||||
// -1 (variable not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the decoded variable).
|
||||
// -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
|
||||
// NULL or zero length.
|
||||
@@ -310,38 +320,38 @@ int mg_get_var(const char *data, size_t data_len,
|
||||
// Return:
|
||||
// On success, value length.
|
||||
// On error:
|
||||
// -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the value).
|
||||
int mg_get_cookie(const struct mg_connection *,
|
||||
const char *cookie_name, char *buf, size_t buf_len);
|
||||
// -1 (either "Cookie:" header is not present at all or the requested
|
||||
// parameter is not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the
|
||||
// value).
|
||||
int mg_get_cookie(const char *cookie, const char *var_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.
|
||||
@@ -362,6 +372,14 @@ const char *mg_get_builtin_mime_type(const char *file_name);
|
||||
// Return Mongoose version.
|
||||
const char *mg_version(void);
|
||||
|
||||
// URL-decode input buffer into destination buffer.
|
||||
// 0-terminate the destination buffer.
|
||||
// form-url-encoded data differs from URI encoding in a way that it
|
||||
// uses '+' as character for space, see RFC 1866 section 8.2.1
|
||||
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
|
||||
// Return: length of the decoded data, or -1 if dst buffer is too small.
|
||||
int mg_url_decode(const char *src, int src_len, char *dst,
|
||||
int dst_len, int is_form_url_encoded);
|
||||
|
||||
// MD5 hash given strings.
|
||||
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||
@@ -369,7 +387,7 @@ const char *mg_version(void);
|
||||
// MD5 hash. Example:
|
||||
// char buf[33];
|
||||
// mg_md5(buf, "aa", "bb", NULL);
|
||||
void mg_md5(char buf[33], ...);
|
||||
char *mg_md5(char buf[33], ...);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
print "Content-Type: text/plain\n\nhello\n";
|
||||
+54
-37
@@ -1,40 +1,57 @@
|
||||
<html>
|
||||
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
|
||||
<?
|
||||
HTTP/1.0 200 OK
|
||||
Content-Type: text/html
|
||||
|
||||
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> ') 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><body>
|
||||
|
||||
|
||||
</html>
|
||||
<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>
|
||||
<p> Today is <? mg.write(os.date("%A")) ?>
|
||||
|
||||
<pre>
|
||||
<?
|
||||
-- for k,v in pairs(_G) do mg.write(k, '\n') end
|
||||
|
||||
-- Open database
|
||||
local db = sqlite3.open('requests.db')
|
||||
|
||||
-- Setup a trace callback, to show SQL statements we'll be executing.
|
||||
-- db:trace(function(data, sql) mg.write('Executing: ', sql: '\n') end, nil)
|
||||
|
||||
-- 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,
|
||||
addr
|
||||
);
|
||||
]])
|
||||
|
||||
|
||||
-- Add entry about this request
|
||||
local stmt = db:prepare(
|
||||
'INSERT INTO requests VALUES(NULL, datetime("now"), ?, ?, ?);');
|
||||
stmt:bind_values(mg.request_info.request_method,
|
||||
mg.request_info.uri,
|
||||
mg.request_info.remote_port)
|
||||
stmt:step()
|
||||
stmt:finalize()
|
||||
|
||||
-- Show all previous records
|
||||
mg.write('Previous requests:\n')
|
||||
stmt = db:prepare('SELECT * FROM requests ORDER BY id DESC;')
|
||||
while stmt:step() == sqlite3.ROW do
|
||||
local v = stmt:get_values()
|
||||
mg.write(v[1] .. ' ' .. v[2] .. ' ' .. v[3] .. ' '
|
||||
.. v[4] .. ' ' .. v[5] .. '\n')
|
||||
end
|
||||
|
||||
-- Close database
|
||||
db:close()
|
||||
?>
|
||||
</pre></body></html>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="Makefile" -->
|
||||
<!--#include file="../build/Makefile" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="/etc/passwd" -->
|
||||
<!--#include abspath="/etc/passwd" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include file="c:\boot.ini" -->
|
||||
<!--#include abspath="c:\boot.ini" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
|
||||
<html><pre>
|
||||
ssi_begin
|
||||
<!--#include "embed.c" -->
|
||||
<!--#include "unit_test.c" -->
|
||||
ssi_end
|
||||
</pre></html>
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
ssi_begin
|
||||
<!--#include file="Makefile" -->
|
||||
<!--#include file="../build/Makefile" -->
|
||||
ssi_end
|
||||
+37
-104
@@ -4,6 +4,8 @@
|
||||
|
||||
use IO::Socket;
|
||||
use File::Path;
|
||||
use File::Basename;
|
||||
use Cwd;
|
||||
use strict;
|
||||
use warnings;
|
||||
#use diagnostics;
|
||||
@@ -16,12 +18,15 @@ my $num_requests;
|
||||
my $dir_separator = on_windows() ? '\\' : '/';
|
||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||
my $test_dir_uri = "test_dir";
|
||||
my $root = 'test';
|
||||
my $test_dir = $root . $dir_separator. $test_dir_uri;
|
||||
my $root = '../test';
|
||||
my $abs_root = Cwd::abs_path(dirname($0) . $dir_separator . $root);
|
||||
my $test_dir = $abs_root . $dir_separator. $test_dir_uri;
|
||||
#print "$test_dir\n"; exit 0;
|
||||
my $config = 'mongoose.conf';
|
||||
my $exe = '.' . $dir_separator . 'mongoose';
|
||||
my $embed_exe = '.' . $dir_separator . 'embed';
|
||||
my $unit_test_exe = '.' . $dir_separator . 'unit_test';
|
||||
my $exe_ext = on_windows() ? '.exe' : '';
|
||||
my $mongoose_exe = '.' . $dir_separator . 'mongoose' . $exe_ext;
|
||||
my $embed_exe = '.' . $dir_separator . 'embed' . $exe_ext;
|
||||
my $unit_test_exe = '.' . $dir_separator . 'unit_test' . $exe_ext;
|
||||
my $exit_code = 0;
|
||||
|
||||
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
|
||||
@@ -51,9 +56,9 @@ sub get_num_of_log_entries {
|
||||
# Send the request to the 127.0.0.1:$port and return the reply
|
||||
sub req {
|
||||
my ($request, $inc, $timeout) = @_;
|
||||
my $sock = IO::Socket::INET->new(Proto=>"tcp",
|
||||
PeerAddr=>'127.0.0.1', PeerPort=>$port);
|
||||
fail("Cannot connect: $!") unless $sock;
|
||||
my $sock = IO::Socket::INET->new(Proto => 6,
|
||||
PeerAddr => '127.0.0.1', PeerPort => $port);
|
||||
fail("Cannot connect to http://127.0.0.1:$port : $!") unless $sock;
|
||||
$sock->autoflush(1);
|
||||
foreach my $byte (split //, $request) {
|
||||
last unless print $sock $byte;
|
||||
@@ -97,7 +102,6 @@ sub spawn {
|
||||
if (on_windows()) {
|
||||
my @args = split /\s+/, $cmdline;
|
||||
my $executable = $args[0];
|
||||
$executable .= '.exe';
|
||||
Win32::Spawn($executable, $cmdline, $pid);
|
||||
die "Cannot spawn @_: $!" unless $pid;
|
||||
} else {
|
||||
@@ -146,11 +150,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,23 +157,25 @@ 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");
|
||||
o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
|
||||
write_file($config, "access_log_file access.log\n" .
|
||||
"document_root $root\n" .
|
||||
"listening_ports 127.0.0.1:12345\n");
|
||||
spawn("$mongoose_exe -listening_ports 127.0.0.1:$port");
|
||||
o("GET /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 ".
|
||||
my $cmd = "$mongoose_exe ".
|
||||
"-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 " .
|
||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||
'-put_delete_auth_file test/passfile ' .
|
||||
"-put_delete_auth_file $abs_root/passfile " .
|
||||
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
||||
"-document_root $root ".
|
||||
"-hide_files_patterns **exploit.pl ".
|
||||
"-hide_files_patterns **exploit.PL ".
|
||||
"-enable_keep_alive yes ".
|
||||
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
||||
@@ -219,11 +220,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');
|
||||
@@ -248,8 +248,9 @@ write_file($path, read_file($root . $dir_separator . 'env.cgi'));
|
||||
chmod(0755, $path);
|
||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
||||
'index.cgi execution');
|
||||
|
||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
|
||||
"SCRIPT_FILENAME=test/test_dir/x/index.cgi", 'SCRIPT_FILENAME');
|
||||
"SCRIPT_FILENAME=$test_dir/x/index.cgi", 'SCRIPT_FILENAME');
|
||||
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
|
||||
'Aliases SCRIPT_NAME');
|
||||
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
|
||||
@@ -357,6 +358,8 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
||||
unlink "$root/.htpasswd";
|
||||
|
||||
|
||||
o("GET /dir%20with%20spaces/hello.cgi HTTP/1.0\n\r\n",
|
||||
'HTTP/1.1 200 OK.+hello', 'CGI script with spaces in path');
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", 'HTTP/1.1 200 OK', 'GET CGI file');
|
||||
o("GET /bad2.cgi HTTP/1.0\n\n", "HTTP/1.1 123 Please pass me to the client\r",
|
||||
'CGI Status code text');
|
||||
@@ -387,6 +390,8 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
||||
o("GET /env.cgi HTTP/1.0\n\r\n", '\nCGI_BAZ=baz\n', '-cgi_env 3');
|
||||
o("GET /env.cgi/a/b/98 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/98\n', 'PATH_INFO');
|
||||
o("GET /env.cgi/a/b/9 HTTP/1.0\n\r\n", 'PATH_INFO=/a/b/9\n', 'PATH_INFO');
|
||||
o("GET /env.cgi/foo/bar?a=b HTTP/1.0\n\n",
|
||||
'SCRIPT_NAME=/env.cgi\s', 'SCRIPT_NAME for CGI with PATH_INFO');
|
||||
|
||||
# Check that CGI's current directory is set to script's directory
|
||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||
@@ -406,7 +411,7 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
||||
my $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
|
||||
my $word = on_windows() ? 'boot loader' : 'root';
|
||||
o("GET /$abs_path HTTP/1.0\n\n",
|
||||
"ssi_begin.+$word.+ssi_end", 'SSI #include file= (absolute)');
|
||||
"ssi_begin.+$word.+ssi_end", 'SSI #include abspath');
|
||||
o("GET /ssi7.shtml HTTP/1.0\n\n",
|
||||
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
||||
o("GET /ssi8.shtml HTTP/1.0\n\n",
|
||||
@@ -415,9 +420,9 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
|
||||
# Manipulate the passwords file
|
||||
my $path = 'test_htpasswd';
|
||||
unlink $path;
|
||||
system("$exe -A $path a b c") == 0
|
||||
system("$mongoose_exe -A $path a b c") == 0
|
||||
or fail("Cannot add user in a passwd file");
|
||||
system("$exe -A $path a b c2") == 0
|
||||
system("$mongoose_exe -A $path a b c2") == 0
|
||||
or fail("Cannot edit user in a passwd file");
|
||||
my $content = read_file($path);
|
||||
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
|
||||
@@ -426,7 +431,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 +460,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() ? 'wi' : 'un';
|
||||
system("make $target") == 0 or fail("Unit test failed!");
|
||||
}
|
||||
|
||||
print "SUCCESS! All tests passed.\n";
|
||||
+517
-130
@@ -1,30 +1,87 @@
|
||||
// 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
|
||||
|
||||
#ifndef _WIN32
|
||||
#define __cdecl
|
||||
#define USE_IPV6
|
||||
#endif
|
||||
|
||||
// USE_* definitions must be made before #include "mongoose.c" !
|
||||
|
||||
#include "mongoose.c"
|
||||
|
||||
#define FATAL(str, line) do { \
|
||||
static int s_total_tests = 0;
|
||||
static int s_failed_tests = 0;
|
||||
|
||||
#define FAIL(str, line) do { \
|
||||
printf("Fail on line %d: [%s]\n", line, str); \
|
||||
abort(); \
|
||||
s_failed_tests++; \
|
||||
} while (0)
|
||||
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
|
||||
|
||||
#define UNUSED_PORT "33796"
|
||||
#define ASSERT(expr) do { \
|
||||
s_total_tests++; \
|
||||
if (!(expr)) FAIL(#expr, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
static void test_parse_http_request() {
|
||||
#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_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";
|
||||
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
|
||||
|
||||
ASSERT(parse_http_request(req1, sizeof(req1), &ri) == sizeof(req1) - 1);
|
||||
ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1);
|
||||
ASSERT(ri.num_headers == 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 +90,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 +105,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 +118,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;
|
||||
@@ -102,7 +162,8 @@ static void test_match_prefix(void) {
|
||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b/") == -1);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.a") == 6);
|
||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/B.A") == 6);
|
||||
ASSERT(match_prefix("**o$", 4, "HELLO") == 5);
|
||||
}
|
||||
|
||||
static void test_remove_double_dots() {
|
||||
@@ -121,106 +182,284 @@ 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_filename2 = "upload_test2.txt";
|
||||
static const char *upload_ok_message = "upload successful";
|
||||
|
||||
if (event == MG_NEW_REQUEST && !strcmp(request_info->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);
|
||||
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 test_mg_fetch(void) {
|
||||
static const char *options[] = {
|
||||
"document_root", ".",
|
||||
"listening_ports", UNUSED_PORT,
|
||||
NULL,
|
||||
};
|
||||
char buf[2000], buf2[2000];
|
||||
int n, length;
|
||||
static void upload_cb(struct mg_connection *conn, const char *path) {
|
||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
char *p1, *p2;
|
||||
int len1, len2;
|
||||
|
||||
if (atoi(ri->query_string) == 1) {
|
||||
ASSERT(!strcmp(path, "./upload_test.txt"));
|
||||
ASSERT((p1 = read_file("main.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);
|
||||
} else if (atoi(ri->query_string) == 2) {
|
||||
if (!strcmp(path, "./upload_test.txt")) {
|
||||
ASSERT((p1 = read_file("lua_5.2.1.h", &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);
|
||||
} else if (!strcmp(path, "./upload_test2.txt")) {
|
||||
ASSERT((p1 = read_file("mod_lua.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);
|
||||
} else {
|
||||
ASSERT(0);
|
||||
}
|
||||
} else {
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
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-Type: text/plain\r\n\r\n"
|
||||
"%s", fetch_data);
|
||||
close_connection(conn);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcmp(ri->uri, "/upload")) {
|
||||
ASSERT(ri->query_string != NULL);
|
||||
ASSERT(mg_upload(conn, ".") == atoi(ri->query_string));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, NULL, NULL
|
||||
};
|
||||
|
||||
static const char *OPTIONS[] = {
|
||||
"document_root", ".",
|
||||
"listening_ports", LISTENING_ADDR,
|
||||
"enable_keep_alive", "yes",
|
||||
"ssl_certificate", "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 main.c, should succeed
|
||||
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||
"GET /main.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("main.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 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);
|
||||
// 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 in-memory data with no Content-Length, should succeed.
|
||||
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||
"GET /data HTTP/1.1\r\n\r\n")) != NULL);
|
||||
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
||||
ASSERT(len1 == (int) strlen(fetch_data));
|
||||
ASSERT(memcmp(p1, fetch_data, len1) == 0);
|
||||
free(p1);
|
||||
mg_close_connection(conn);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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, *file2_data, *post_data;
|
||||
int file_len, file2_len, post_data_len;
|
||||
|
||||
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
|
||||
|
||||
// Upload one file
|
||||
ASSERT((file_data = read_file("main.c", &file_len)) != NULL);
|
||||
post_data = 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?1 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);
|
||||
|
||||
// Upload two files
|
||||
ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL);
|
||||
ASSERT((file2_data = read_file("mod_lua.c", &file2_len)) != NULL);
|
||||
post_data = NULL;
|
||||
post_data_len = alloc_printf(&post_data, 0,
|
||||
// First file
|
||||
"--%s\r\n"
|
||||
"Content-Disposition: form-data; "
|
||||
"name=\"file\"; "
|
||||
"filename=\"%s\"\r\n\r\n"
|
||||
"%.*s\r\n"
|
||||
|
||||
// Second file
|
||||
"--%s\r\n"
|
||||
"Content-Disposition: form-data; "
|
||||
"name=\"file\"; "
|
||||
"filename=\"%s\"\r\n\r\n"
|
||||
"%.*s\r\n"
|
||||
|
||||
// Final boundary
|
||||
"--%s--\r\n",
|
||||
boundary, upload_filename,
|
||||
file_len, file_data,
|
||||
boundary, upload_filename2,
|
||||
file2_len, file2_data,
|
||||
boundary);
|
||||
ASSERT(post_data_len > 0);
|
||||
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
|
||||
ebuf, sizeof(ebuf),
|
||||
"POST /upload?2 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(file2_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);
|
||||
|
||||
remove(tmp_file);
|
||||
mg_stop(ctx);
|
||||
}
|
||||
|
||||
@@ -232,7 +471,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 +535,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 +549,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;
|
||||
|
||||
@@ -326,39 +562,22 @@ static void test_lua(void) {
|
||||
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, "mg.request_info.request_method", "POST");
|
||||
check_lua_expr(L, "mg.request_info.uri", "/foo/bar");
|
||||
check_lua_expr(L, "mg.request_info.num_headers", "2");
|
||||
check_lua_expr(L, "mg.request_info.remote_ip", "0");
|
||||
check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12");
|
||||
check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close");
|
||||
(void) luaL_dostring(L, "post = mg.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 +599,189 @@ 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);
|
||||
}
|
||||
|
||||
static void test_url_decode(void) {
|
||||
char buf[100];
|
||||
|
||||
ASSERT(mg_url_decode("foo", 3, buf, 3, 0) == -1); // No space for \0
|
||||
ASSERT(mg_url_decode("foo", 3, buf, 4, 0) == 3);
|
||||
ASSERT(strcmp(buf, "foo") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 0) == 2);
|
||||
ASSERT(strcmp(buf, "a+") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("a+", 2, buf, sizeof(buf), 1) == 2);
|
||||
ASSERT(strcmp(buf, "a ") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 1, buf, sizeof(buf), 1) == 1);
|
||||
ASSERT(strcmp(buf, "%") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 2, buf, sizeof(buf), 1) == 2);
|
||||
ASSERT(strcmp(buf, "%6") == 0);
|
||||
|
||||
ASSERT(mg_url_decode("%61", 3, buf, sizeof(buf), 1) == 1);
|
||||
ASSERT(strcmp(buf, "a") == 0);
|
||||
}
|
||||
|
||||
static void test_mg_strcasestr(void) {
|
||||
static const char *big1 = "abcdef";
|
||||
ASSERT(mg_strcasestr("Y", "X") == NULL);
|
||||
ASSERT(mg_strcasestr("Y", "y") != NULL);
|
||||
ASSERT(mg_strcasestr(big1, "X") == NULL);
|
||||
ASSERT(mg_strcasestr(big1, "CD") == big1 + 2);
|
||||
ASSERT(mg_strcasestr("aa", "AAB") == NULL);
|
||||
}
|
||||
|
||||
static void test_mg_get_cookie(void) {
|
||||
char buf[20];
|
||||
|
||||
ASSERT(mg_get_cookie("", "foo", NULL, sizeof(buf)) == -2);
|
||||
ASSERT(mg_get_cookie("", "foo", buf, 0) == -2);
|
||||
ASSERT(mg_get_cookie("", "foo", buf, sizeof(buf)) == -1);
|
||||
ASSERT(mg_get_cookie("", NULL, buf, sizeof(buf)) == -1);
|
||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "a", buf, sizeof(buf)) == 1);
|
||||
ASSERT(strcmp(buf, "1") == 0);
|
||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "b", buf, sizeof(buf)) == 1);
|
||||
ASSERT(strcmp(buf, "2") == 0);
|
||||
ASSERT(mg_get_cookie("a=1; b=123", "b", buf, sizeof(buf)) == 3);
|
||||
ASSERT(strcmp(buf, "123") == 0);
|
||||
ASSERT(mg_get_cookie("a=1; b=2; c; d", "c", buf, sizeof(buf)) == -1);
|
||||
}
|
||||
|
||||
static void test_strtoll(void) {
|
||||
ASSERT(strtoll("0", NULL, 10) == 0);
|
||||
ASSERT(strtoll("123", NULL, 10) == 123);
|
||||
ASSERT(strtoll("-34", NULL, 10) == -34);
|
||||
ASSERT(strtoll("3566626116", NULL, 10) == 3566626116);
|
||||
}
|
||||
|
||||
static void test_parse_port_string(void) {
|
||||
static const char *valid[] = {
|
||||
"1", "1s", "1r", "1.2.3.4:1", "1.2.3.4:1s", "1.2.3.4:1r",
|
||||
#if defined(USE_IPV6)
|
||||
"[::1]:123", "[3ffe:2a00:100:7031::1]:900",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
static const char *invalid[] = {
|
||||
"0", "99999", "1k", "1.2.3", "1.2.3.4:", "1.2.3.4:2p",
|
||||
NULL
|
||||
};
|
||||
struct socket so;
|
||||
struct vec vec;
|
||||
int i;
|
||||
|
||||
for (i = 0; valid[i] != NULL; i++) {
|
||||
vec.ptr = valid[i];
|
||||
vec.len = strlen(vec.ptr);
|
||||
ASSERT(parse_port_string(&vec, &so) != 0);
|
||||
}
|
||||
|
||||
for (i = 0; invalid[i] != NULL; i++) {
|
||||
vec.ptr = invalid[i];
|
||||
vec.len = strlen(vec.ptr);
|
||||
ASSERT(parse_port_string(&vec, &so) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
int __cdecl main(void) {
|
||||
test_parse_port_string();
|
||||
test_mg_strcasestr();
|
||||
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();
|
||||
test_url_decode();
|
||||
test_mg_get_cookie();
|
||||
test_strtoll();
|
||||
#ifdef USE_LUA
|
||||
test_lua();
|
||||
#endif
|
||||
test_skip_quoted();
|
||||
return 0;
|
||||
|
||||
printf("TOTAL TESTS: %d, FAILED: %d\n", s_total_tests, s_failed_tests);
|
||||
|
||||
return s_failed_tests == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
Reference in new issue
Block a user