mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-25 12:04:47 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3070e8572d | ||
|
|
baf4d8af27 | ||
|
|
d5acf2e2a7 | ||
|
|
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
|
This code is dual-licensed: you can redistribute it and/or modify
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
it under the terms of the GNU General Public License version 2 as
|
||||||
in the Software without restriction, including without limitation the rights
|
published by the Free Software Foundation. For the terms of this
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
license, see <http://www.gnu.org/licenses/>.
|
||||||
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
|
You are free to use this code under the terms of the GNU General
|
||||||
all copies or substantial portions of the Software.
|
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
|
Alternatively, you can license this code under a commercial
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
license, as set out in <http://cesanta.com/products.html>.
|
||||||
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.
|
|
||||||
@@ -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
|
Project mission is to provide easy to use, powerful, embeddable web server.
|
||||||
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.
|
|
||||||
|
|
||||||
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:
|
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
|
- 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
|
- Support for CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV
|
||||||
- Lua server pages (PHP-like functionality using Lua), see
|
- Resumed download, URL rewrite, file blacklist, IP-based ACL, Windows service
|
||||||
[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)
|
|
||||||
- Download speed limit based on client subnet or URI pattern
|
- Download speed limit based on client subnet or URI pattern
|
||||||
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
|
|
||||||
- 130 kilobytes Windows executable with all of the above and no dependencies
|
|
||||||
- Simple and clean embedding API,
|
- 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
|
The source is in single
|
||||||
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
|
[mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file
|
||||||
to make things easy.
|
to make things easy. Embedding examples:
|
||||||
- Embedding examples:
|
|
||||||
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
|
[hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c),
|
||||||
[post.c](https://github.com/valenok/mongoose/blob/master/examples/post.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),
|
[upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c),
|
||||||
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
[websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||||
- Extensive documentation in form of
|
- HTTP client capable of sending arbitrary HTTP/HTTPS requests
|
||||||
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
- [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
|
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.
|
mailing list.
|
||||||
|
|
||||||
|
[](http://githalytics.com/valenok/mongoose)
|
||||||
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).
|
|
||||||
|
|
||||||
|
|
||||||
If you feel grateful for the stuff I've done, you can buy me a book from my
|
# License
|
||||||
[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,
|
Mongoose is dual-licensed. It is available under the terms of
|
||||||
L.E.Spencer, and 4 others. Appreciated guys, you keep my brains going!
|
[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 @@
|
|||||||
|
# Overview
|
||||||
**NOTE: THIS MANUAL IS WORK IN PROGRESS**
|
|
||||||
|
|
||||||
Overview
|
|
||||||
--------
|
|
||||||
|
|
||||||
Mongoose is small and easy to use web server. It is self-contained, and does
|
Mongoose is small and easy to use web server. It is self-contained, and does
|
||||||
not require any external software to run.
|
not require any external software to run.
|
||||||
|
|
||||||
On Windows, mongoose iconifies itself to the system tray icon when started.
|
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
|
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
|
On UNIX and Mac, mongoose is a command line utility. Running `mongoose` in
|
||||||
terminal, optionally followed by configuration parameters
|
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.
|
If configuration file is specified explicitly in the command line, i.e.
|
||||||
`mongoose path_to_config_file`, then specified configuration file is used.
|
`mongoose path_to_config_file`, then specified configuration file is used.
|
||||||
Otherwise, mongoose would search for file `mongoose.conf` in the same directory
|
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
|
Configuration file is a sequence of lines, each line containing
|
||||||
command line argument name and it's value. Empty lines, and lines beginning
|
command line argument name and it's value. Empty lines, and lines beginning
|
||||||
with `#`, are ignored. Here is the example of `mongoose.conf` file:
|
with `#`, are ignored. Here is the example of `mongoose.conf` file:
|
||||||
|
|
||||||
# mongoose.conf file
|
|
||||||
document_root c:\www
|
document_root c:\www
|
||||||
listening_ports 8080,8043s
|
listening_ports 8080,8043s
|
||||||
ssl_certificate c:\mongoose\ssl_cert.pem
|
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
|
`document_root /var/www`, and mongoose has been started as
|
||||||
`mongoose -document_root /etc`, then `/etc` directory will be served as
|
`mongoose -document_root /etc`, then `/etc` directory will be served as
|
||||||
document root, because command line options take priority over
|
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 can also be used to modify `.htpasswd` passwords file:
|
||||||
|
|
||||||
mongoose -A <htpasswd_file> <realm> <user> <passwd>
|
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
|
** Matches everything
|
||||||
double-click the executable. The folder should be accessible via
|
* Matches everything but slash character, '/'
|
||||||
[http://localhost:8080](http://localhost:8080) in any browser.
|
? 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
|
All other characters in the pattern match themselves. Examples:
|
||||||
directory `/var/www`: put this line in the system startup script:
|
|
||||||
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
|
||||||
|
|
||||||
- How to serve user home directories using URL rewrite:
|
**.cgi$ Any string that ends with .cgi
|
||||||
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
|
/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
|
Below is a list of configuration options Mongoose understands. Every option
|
||||||
Add/edit user's password in the passwords file. Deleting users
|
is followed by it's default value. If default value is not present, then
|
||||||
can be done with any text editor. Functionality is similar to
|
it is empty.
|
||||||
Apache's htdigest utility.
|
|
||||||
|
|
||||||
-C cgi_pattern
|
### cgi_pattern `**.cgi$|**.pl$|**.php$`
|
||||||
All files that fully match cgi_pattern are treated as CGI.
|
All files that match `cgi_pattern` are treated as CGI files. Default pattern
|
||||||
Default pattern allows CGI files be anywhere. To restrict CGIs to
|
allows CGI files be anywhere. To restrict CGIs to a certain directory,
|
||||||
certain directory, use e.g. "-C /cgi-bin/**.cgi". Default:
|
use `/path/to/cgi-bin/**.cgi` as pattern. Note that full file path is
|
||||||
"**.cgi$|**.pl$|**.php$"
|
matched against the pattern, not the URI.
|
||||||
|
|
||||||
-E cgi_environment
|
### cgi_environment
|
||||||
Extra environment variables to be passed to the CGI script in
|
Extra environment variables to be passed to the CGI script in
|
||||||
addition to standard ones. The list must be comma-separated list
|
addition to standard ones. The list must be comma-separated list
|
||||||
of X=Y pairs, like this: "VARIABLE1=VALUE1,VARIABLE2=VALUE2".
|
of name=value pairs, like this: `VARIABLE1=VALUE1,VARIABLE2=VALUE2`.
|
||||||
Default: ""
|
|
||||||
|
|
||||||
-G put_delete_passwords_file
|
### put\_delete\_auth\_file
|
||||||
PUT and DELETE passwords file. This must be specified if PUT or
|
Passwords file for PUT and DELETE requests. Without it, PUT and DELETE requests
|
||||||
DELETE methods are used. Default: ""
|
will fail.
|
||||||
|
|
||||||
-I cgi_interpreter
|
### cgi_interpreter
|
||||||
Use cgi_interpreter as a CGI interpreter for all CGI scripts
|
Path to an executable to use as CGI interpreter for __all__ CGI scripts
|
||||||
regardless script extension. Mongoose decides which interpreter
|
regardless script extension. If this option is not set (which is a default),
|
||||||
to use by looking at the first line of a CGI script. 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
|
For example, if both PHP and perl CGIs are used, then
|
||||||
Comma separated list of URI=PATH pairs, specifying that given
|
`#!/path/to/php-cgi.exe` and `#!/path/to/perl.exe` must be first lines of the
|
||||||
URIs must be protected with respected password files. Default: ""
|
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
|
If all CGIs use the same interpreter, for example they are all PHP, then
|
||||||
Authorization realm. Default: "mydomain.com"
|
`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
|
### protect_uri
|
||||||
All files that fully match ssi_pattern are treated as SSI.
|
Comma separated list of URI=PATH pairs, specifying that given
|
||||||
Unknown SSI directives are silently ignored. Currently, two SSI
|
URIs must be protected with respected password files. Paths must be full
|
||||||
directives are supported, "include" and "exec". Default:
|
file paths.
|
||||||
"**.shtml$|**.shtm$"
|
|
||||||
|
|
||||||
-T throttle
|
### authentication_domain `mydomain.com`
|
||||||
Limit download speed for clients. throttle is a comma-separated
|
Authorization realm used in `.htpasswd` authorization.
|
||||||
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: ""
|
|
||||||
|
|
||||||
-a access_log_file
|
### ssi_pattern `**.shtml$|**.shtm$`
|
||||||
Access log file. Default: "", no logging is done.
|
All files that match `ssi_pattern` are treated as SSI.
|
||||||
|
|
||||||
-d enable_directory_listing
|
Server Side Includes (SSI) is a simple interpreted server-side scripting
|
||||||
Enable/disable directory listing. Default: "yes"
|
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
|
In order for a webpage to recognize an SSI-enabled HTML file, the filename
|
||||||
Error log file. Default: "", no errors are logged.
|
should end with a special extension, by default the extension should be
|
||||||
|
either `.shtml` or `.shtm`.
|
||||||
|
|
||||||
-g global_passwords_file
|
Unknown SSI directives are silently ignored by mongoose. Currently, two SSI
|
||||||
Location of a global passwords file. If set, per-directory
|
directives are supported, `<!--#include ...>` and
|
||||||
.htpasswd files are ignored, and all requests must be authorised
|
`<!--#exec "command">`. Note that `<!--#include ...>` directive supports
|
||||||
against that file. Default: ""
|
three path specifications:
|
||||||
|
|
||||||
-i index_files
|
<!--#include virtual="path"> Path is relative to web server root
|
||||||
Comma-separated list of files to be treated as directory index
|
<!--#include abspath="path"> Path is absolute or relative to
|
||||||
files. Default: "index.html,index.htm,index.cgi"
|
web server working dir
|
||||||
|
<!--#include file="path">, Path is relative to current document
|
||||||
|
<!--#include "path">
|
||||||
|
|
||||||
-l access_control_list
|
The `include` directive may be used to include the contents of a file or the
|
||||||
Specify access control list (ACL). ACL is a comma separated list
|
result of running a CGI script. The `exec` directive is used to execute a
|
||||||
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus
|
command on a server, and show command's output. Example:
|
||||||
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: ""
|
|
||||||
|
|
||||||
-m extra_mime_types
|
<!--#exec "ls -l" -->
|
||||||
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: ""
|
|
||||||
|
|
||||||
-p listening_ports
|
For more information on Server Side Includes, take a look at the Wikipedia:
|
||||||
Comma-separated list of ports to listen on. If the port is SSL, a
|
[Server Side Includes](http://en.wikipedia.org/wiki/Server_Side_Includes)
|
||||||
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"
|
|
||||||
|
|
||||||
-r document_root
|
### throttle
|
||||||
Location of the WWW root directory. Default: "."
|
Limit download speed for clients. `throttle` is a comma-separated
|
||||||
|
list of key=value pairs, where key could be:
|
||||||
|
|
||||||
-s ssl_certificate
|
* limit speed for all connections
|
||||||
Location of SSL certificate file. Default: ""
|
x.x.x.x/mask limit speed for specified subnet
|
||||||
|
uri_prefix_pattern limit speed for given URIs
|
||||||
|
|
||||||
-t num_threads
|
The value is a floating-point number of bytes per second, optionally
|
||||||
Number of worker threads to start. Default: "10"
|
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
|
*=1k,10.0.0.0/8=0 limit all accesses to 1 kilobyte per second,
|
||||||
Switch to given user's credentials after startup. Default: ""
|
but give connections from 10.0.0.0/8 subnet
|
||||||
|
unlimited speed
|
||||||
|
|
||||||
-w url_rewrite_patterns
|
/downloads/=5k limit accesses to all URIs in `/downloads/` to
|
||||||
Comma-separated list of URL rewrites in the form of "pattern=sub-
|
5 kilobytes per secods. All other accesses are unlimited
|
||||||
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: ""
|
|
||||||
|
|
||||||
-x hide_files_patterns
|
### access\_log\_file
|
||||||
A prefix pattern for the files to hide. Files that match the pat-
|
Path to a file for access logs. Either full path, or relative to current
|
||||||
tern will not show up in directory listing and return 404 Not
|
working directory. If absent (default), then accesses are not logged.
|
||||||
Found if requested. Default: ""
|
|
||||||
|
|
||||||
```
|
### 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
|
- Presentation made by Arnout Vandecappelle at FOSDEM 2011 on 2011-02-06
|
||||||
in Brussels, Belgium, called
|
in Brussels, Belgium, called
|
||||||
"Creating secure web based user interfaces for Embedded Devices"
|
"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">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key> <string>Mongoose</string>
|
||||||
<string>mongoose</string>
|
<key>CFBundlePackageType</key> <string>APPL</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundleTypeRole</key> <string>None</string>
|
||||||
<string>APPL</string>
|
<key>CFBundleIconFiles</key> <array>
|
||||||
<key>CFBundleTypeRole</key>
|
<string>mongoose_16x16.png</string>
|
||||||
<string>None</string>
|
<string>mongoose_22x22.png</string>
|
||||||
<key>CFBundleIconFiles</key>
|
<string>mongoose_32x32.png</string>
|
||||||
<array>
|
<string>mongoose_64x64.png</string>
|
||||||
<string>mongoose_16x16.png</string>
|
</array>
|
||||||
</array>
|
<key>LSUIElement</key> <true/>
|
||||||
<key>LSUIElement</key>
|
<key>RunAtLoad</key> <true/>
|
||||||
<true/>
|
<key>Label</key> <string>com.kolkin.mongoose</string>
|
||||||
|
<key>ProgramArguments</key> <array> </array>
|
||||||
|
<key>KeepAlive</key> <true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
+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 <windows.h>
|
||||||
#include <winsvc.h>
|
#include <winsvc.h>
|
||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
#define PATH_MAX MAX_PATH
|
#define PATH_MAX MAX_PATH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef S_ISDIR
|
||||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DIRSEP '\\'
|
#define DIRSEP '\\'
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#define vsnprintf _vsnprintf
|
#define vsnprintf _vsnprintf
|
||||||
#define sleep(x) Sleep((x) * 1000)
|
#define sleep(x) Sleep((x) * 1000)
|
||||||
#define WINCDECL __cdecl
|
#define WINCDECL __cdecl
|
||||||
|
#define abs_path(rel, abs, abs_size) _fullpath((abs), (rel), (abs_size))
|
||||||
#else
|
#else
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#define DIRSEP '/'
|
#define DIRSEP '/'
|
||||||
#define WINCDECL
|
#define WINCDECL
|
||||||
|
#define abs_path(rel, abs, abs_size) realpath((rel), (abs))
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
#define MAX_OPTIONS 40
|
#define MAX_OPTIONS 100
|
||||||
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
#define MAX_CONF_FILE_LINE_SIZE (8 * 1024)
|
||||||
|
|
||||||
static int exit_flag;
|
static int exit_flag;
|
||||||
@@ -68,7 +77,15 @@ static struct mg_context *ctx; // Set by start_mongoose()
|
|||||||
#endif /* !CONFIG_FILE */
|
#endif /* !CONFIG_FILE */
|
||||||
|
|
||||||
static void WINCDECL signal_handler(int sig_num) {
|
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, ...) {
|
static void die(const char *fmt, ...) {
|
||||||
@@ -92,22 +109,19 @@ static void show_usage_and_exit(void) {
|
|||||||
const char **names;
|
const char **names;
|
||||||
int i;
|
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__);
|
mg_version(), __DATE__);
|
||||||
fprintf(stderr, "Usage:\n");
|
fprintf(stderr, "Usage:\n");
|
||||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\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, " mongoose [-option value ...]\n");
|
||||||
fprintf(stderr, "\nOPTIONS:\n");
|
fprintf(stderr, "\nOPTIONS:\n");
|
||||||
|
|
||||||
names = mg_get_valid_option_names();
|
names = mg_get_valid_option_names();
|
||||||
for (i = 0; names[i] != NULL; i += 3) {
|
for (i = 0; names[i] != NULL; i += 2) {
|
||||||
fprintf(stderr, " -%s %s (default: \"%s\")\n",
|
fprintf(stderr, " -%s %s\n",
|
||||||
names[i], names[i + 1], names[i + 2] == NULL ? "" : names[i + 2]);
|
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);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,32 +163,15 @@ static void create_config_file(const char *path) {
|
|||||||
} else if ((fp = fopen(path, "a+")) != NULL) {
|
} else if ((fp = fopen(path, "a+")) != NULL) {
|
||||||
fprintf(fp, "%s", config_file_top_comment);
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
names = mg_get_valid_option_names();
|
names = mg_get_valid_option_names();
|
||||||
for (i = 0; names[i] != NULL; i += 3) {
|
for (i = 0; names[i * 2] != NULL; i++) {
|
||||||
value = mg_get_option(ctx, names[i]);
|
value = mg_get_option(ctx, names[i * 2]);
|
||||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
fprintf(fp, "# %s %s\n", names[i * 2], value ? value : "<value>");
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#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) {
|
static char *sdup(const char *str) {
|
||||||
char *p;
|
char *p;
|
||||||
if ((p = (char *) malloc(strlen(str) + 1)) != NULL) {
|
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) {
|
static void set_option(char **options, const char *name, const char *value) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!strcmp(name, "document_root") || !(strcmp(name, "r"))) {
|
|
||||||
verify_document_root(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
for (i = 0; i < MAX_OPTIONS - 3; i++) {
|
||||||
if (options[i] == NULL) {
|
if (options[i] == NULL) {
|
||||||
options[i] = sdup(name);
|
options[i] = sdup(name);
|
||||||
options[i + 1] = sdup(value);
|
options[i + 1] = sdup(value);
|
||||||
options[i + 2] = NULL;
|
options[i + 2] = NULL;
|
||||||
break;
|
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;
|
FILE *fp = NULL;
|
||||||
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
size_t i, cmd_line_opts_start = 1, line_no = 0;
|
||||||
|
|
||||||
options[0] = NULL;
|
|
||||||
|
|
||||||
// Should we use a config file ?
|
// Should we use a config file ?
|
||||||
if (argv[1] != NULL && argv[1][0] != '-') {
|
if (argv[1] != NULL && argv[1][0] != '-') {
|
||||||
snprintf(config_file, sizeof(config_file), "%s", argv[1]);
|
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);
|
(void) fclose(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle command line flags. They override config file and default settings.
|
// If we're under MacOS and started by launchd, then the second
|
||||||
for (i = cmd_line_opts_start; argv[i] != NULL; i += 2) {
|
// argument is process serial number, -psn_.....
|
||||||
if (argv[i][0] != '-' || argv[i + 1] == NULL) {
|
// In this case, don't process arguments at all.
|
||||||
show_usage_and_exit();
|
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) {
|
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());
|
mg_version());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
|
static int log_message(const struct mg_connection *conn, const char *message) {
|
||||||
if (ev == MG_EVENT_LOG) {
|
(void) conn;
|
||||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
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;
|
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[]) {
|
static void start_mongoose(int argc, char *argv[]) {
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
char *options[MAX_OPTIONS];
|
char *options[MAX_OPTIONS];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -297,15 +358,35 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
show_usage_and_exit();
|
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);
|
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(SIGTERM, signal_handler);
|
||||||
signal(SIGINT, signal_handler);
|
signal(SIGINT, signal_handler);
|
||||||
|
|
||||||
/* Start Mongoose */
|
// Start Mongoose
|
||||||
ctx = mg_start(&mongoose_callback, NULL, (const char **) options);
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.log_message = &log_message;
|
||||||
|
ctx = mg_start(&callbacks, NULL, (const char **) options);
|
||||||
for (i = 0; options[i] != NULL; i++) {
|
for (i = 0; options[i] != NULL; i++) {
|
||||||
free(options[i]);
|
free(options[i]);
|
||||||
}
|
}
|
||||||
@@ -408,16 +489,16 @@ static void save_config(HWND hDlg, FILE *fp) {
|
|||||||
|
|
||||||
fprintf(fp, "%s", config_file_top_comment);
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
options = mg_get_valid_option_names();
|
options = mg_get_valid_option_names();
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
id = ID_CONTROLS + i / 3;
|
id = ID_CONTROLS + i;
|
||||||
if (is_boolean_option(name)) {
|
if (is_boolean_option(name)) {
|
||||||
snprintf(value, sizeof(value), "%s",
|
snprintf(value, sizeof(value), "%s",
|
||||||
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
||||||
} else {
|
} else {
|
||||||
GetDlgItemText(hDlg, id, value, sizeof(value));
|
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 value is the same as default, skip it
|
||||||
if (strcmp(value, default_value) != 0) {
|
if (strcmp(value, default_value) != 0) {
|
||||||
fprintf(fp, "%s %s\n", name, value);
|
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);
|
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
||||||
break;
|
break;
|
||||||
case ID_RESET_DEFAULTS:
|
case ID_RESET_DEFAULTS:
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
value = options[i + 2] == NULL ? "" : options[i + 2];
|
value = options[i * 2 + 1] == NULL ? "" : options[i * 2 + 1];
|
||||||
if (is_boolean_option(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);
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
} else {
|
} else {
|
||||||
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i), value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
if ((is_filename_option(name) || is_directory_option(name)) &&
|
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;
|
OPENFILENAME of;
|
||||||
BROWSEINFO bi;
|
BROWSEINFO bi;
|
||||||
char path[PATH_MAX] = "";
|
char path[PATH_MAX] = "";
|
||||||
@@ -489,7 +570,7 @@ static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (path[0] != '\0') {
|
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);
|
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
||||||
SetWindowText(hDlg, "Mongoose settings");
|
SetWindowText(hDlg, "Mongoose settings");
|
||||||
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
||||||
for (i = 0; options[i] != NULL; i += 3) {
|
for (i = 0; options[i * 2] != NULL; i++) {
|
||||||
name = options[i + 1];
|
name = options[i * 2];
|
||||||
value = mg_get_option(ctx, name);
|
value = mg_get_option(ctx, name);
|
||||||
if (is_boolean_option(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);
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
} else {
|
} else {
|
||||||
SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
SetDlgItemText(hDlg, ID_CONTROLS + i, value == NULL ? "" : value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -585,8 +666,8 @@ static void show_settings_dialog() {
|
|||||||
p = mem + sizeof(dialog_header);
|
p = mem + sizeof(dialog_header);
|
||||||
|
|
||||||
option_names = mg_get_valid_option_names();
|
option_names = mg_get_valid_option_names();
|
||||||
for (i = 0; option_names[i] != NULL; i += 3) {
|
for (i = 0; option_names[i * 2] != NULL; i++) {
|
||||||
long_option_name = option_names[i + 1];
|
long_option_name = option_names[i * 2];
|
||||||
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
||||||
x = 10 + (WIDTH / 2) * (nelems % 2);
|
x = 10 + (WIDTH / 2) * (nelems % 2);
|
||||||
y = (nelems/2 + 1) * HEIGHT + 5;
|
y = (nelems/2 + 1) * HEIGHT + 5;
|
||||||
@@ -604,7 +685,7 @@ static void show_settings_dialog() {
|
|||||||
width -= 20;
|
width -= 20;
|
||||||
cl = 0x81;
|
cl = 0x81;
|
||||||
add_control(&p, dia, 0x80,
|
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,
|
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
||||||
(WORD) (x + width + LABEL_WIDTH + 5),
|
(WORD) (x + width + LABEL_WIDTH + 5),
|
||||||
y, 15, 12, "...");
|
y, 15, 12, "...");
|
||||||
@@ -614,7 +695,7 @@ static void show_settings_dialog() {
|
|||||||
}
|
}
|
||||||
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
||||||
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
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, "");
|
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
||||||
nelems++;
|
nelems++;
|
||||||
}
|
}
|
||||||
@@ -688,6 +769,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
char buf[200], *service_argv[] = {__argv[0], NULL};
|
char buf[200], *service_argv[] = {__argv[0], NULL};
|
||||||
POINT pt;
|
POINT pt;
|
||||||
HMENU hMenu;
|
HMENU hMenu;
|
||||||
|
static UINT s_uTaskbarRestart; // for taskbar creation
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_CREATE:
|
case WM_CREATE:
|
||||||
@@ -698,6 +780,7 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
} else {
|
} else {
|
||||||
start_mongoose(__argc, __argv);
|
start_mongoose(__argc, __argv);
|
||||||
|
s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WM_COMMAND:
|
case WM_COMMAND:
|
||||||
@@ -755,6 +838,9 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
return 0; // We've just sent our own quit message, with proper hwnd.
|
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);
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
||||||
@@ -807,7 +893,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
|||||||
- (void) openBrowser {
|
- (void) openBrowser {
|
||||||
[[NSWorkspace sharedWorkspace]
|
[[NSWorkspace sharedWorkspace]
|
||||||
openURL:[NSURL URLWithString:
|
openURL:[NSURL URLWithString:
|
||||||
[NSString stringWithUTF8String:"http://www.yahoo.com"]]];
|
[NSString stringWithUTF8String:get_url_to_first_open_port(ctx)]]];
|
||||||
}
|
}
|
||||||
- (void) editConfig {
|
- (void) editConfig {
|
||||||
create_config_file(config_file);
|
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:
|
ifeq ($(OS),Windows_NT)
|
||||||
OS=`uname`; \
|
else
|
||||||
test "$$OS" = Linux && LIBS="-ldl" ; \
|
UNAME_S := $(shell uname -s)
|
||||||
$(CC) $(CFLAGS) hello.c ../mongoose.c $$LIBS $(ADD) -o hello;
|
DLL_FLAGS += -shared
|
||||||
$(CC) $(CFLAGS) upload.c ../mongoose.c $$LIBS $(ADD) -o upload;
|
|
||||||
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
ifeq ($(UNAME_S),Linux)
|
||||||
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
CFLAGS += -ldl
|
||||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
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
|
CL = $(MSVC)/bin/cl
|
||||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
||||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf \
|
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf
|
||||||
/link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
LFLAGS = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||||
|
|
||||||
windows:
|
windows:
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) hello.c ../mongoose.c $(CLFLAGS)
|
$(CL) hello.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
$(CL) upload.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
$(CL) post.c ../mongoose.c $(CLFLAGS) $(LFLAGS)
|
||||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
$(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:
|
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.
|
// Get session object for the connection. Caller must hold the lock.
|
||||||
static struct session *get_session(const struct mg_connection *conn) {
|
static struct session *get_session(const struct mg_connection *conn) {
|
||||||
int i;
|
int i;
|
||||||
|
const char *cookie = mg_get_header(conn, "Cookie");
|
||||||
char session_id[33];
|
char session_id[33];
|
||||||
time_t now = time(NULL);
|
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++) {
|
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||||
if (sessions[i].expire != 0 &&
|
if (sessions[i].expire != 0 &&
|
||||||
sessions[i].expire > now &&
|
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,
|
static int begin_request_handler(struct mg_connection *conn) {
|
||||||
struct mg_connection *conn) {
|
|
||||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||||
void *processed = "yes";
|
int processed = 1;
|
||||||
|
|
||||||
if (event == MG_NEW_REQUEST) {
|
if (!request_info->is_ssl) {
|
||||||
if (!request_info->is_ssl) {
|
redirect_to_ssl(conn, request_info);
|
||||||
redirect_to_ssl(conn, request_info);
|
} else if (!is_authorized(conn, request_info)) {
|
||||||
} else if (!is_authorized(conn, request_info)) {
|
redirect_to_login(conn, request_info);
|
||||||
redirect_to_login(conn, request_info);
|
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
||||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
authorize(conn, request_info);
|
||||||
authorize(conn, request_info);
|
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
||||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
ajax_get_messages(conn, request_info);
|
||||||
ajax_get_messages(conn, request_info);
|
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
||||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
ajax_send_message(conn, request_info);
|
||||||
ajax_send_message(conn, request_info);
|
|
||||||
} else {
|
|
||||||
// No suitable handler found, mark as not processed. Mongoose will
|
|
||||||
// try to serve the request.
|
|
||||||
processed = NULL;
|
|
||||||
}
|
|
||||||
} else if (event == MG_EVENT_LOG) {
|
|
||||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
|
||||||
processed = NULL;
|
|
||||||
} else {
|
} else {
|
||||||
processed = NULL;
|
// No suitable handler found, mark as not processed. Mongoose will
|
||||||
|
// try to serve the request.
|
||||||
|
processed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,6 +357,7 @@ static const char *options[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
|
|
||||||
// Initialize random number generator. It will be used later on for
|
// Initialize random number generator. It will be used later on for
|
||||||
@@ -372,7 +365,9 @@ int main(void) {
|
|||||||
srand((unsigned) time(0));
|
srand((unsigned) time(0));
|
||||||
|
|
||||||
// Setup and start Mongoose
|
// Setup and start Mongoose
|
||||||
if ((ctx = mg_start(&event_handler, NULL, options)) == NULL) {
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.begin_request = begin_request_handler;
|
||||||
|
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
|
||||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-21
@@ -2,35 +2,49 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void *callback(enum mg_event event,
|
// This function will be called by mongoose on every new request.
|
||||||
struct mg_connection *conn) {
|
static int begin_request_handler(struct mg_connection *conn) {
|
||||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||||
|
char content[100];
|
||||||
|
|
||||||
if (event == MG_NEW_REQUEST) {
|
// Prepare the message we're going to send
|
||||||
char content[1024];
|
int content_length = snprintf(content, sizeof(content),
|
||||||
int content_length = snprintf(content, sizeof(content),
|
"Hello from mongoose! Remote port: %d",
|
||||||
"Hello from mongoose! Remote port: %d",
|
request_info->remote_port);
|
||||||
request_info->remote_port);
|
|
||||||
mg_printf(conn,
|
// Send HTTP reply to the client
|
||||||
"HTTP/1.1 200 OK\r\n"
|
mg_printf(conn,
|
||||||
"Content-Type: text/plain\r\n"
|
"HTTP/1.1 200 OK\r\n"
|
||||||
"Content-Length: %d\r\n" // Always set Content-Length
|
"Content-Type: text/plain\r\n"
|
||||||
"\r\n"
|
"Content-Length: %d\r\n" // Always set Content-Length
|
||||||
"%s",
|
"\r\n"
|
||||||
content_length, content);
|
"%s",
|
||||||
// Mark as processed
|
content_length, content);
|
||||||
return "";
|
|
||||||
} else {
|
// Returning non-zero tells mongoose that our function has replied to
|
||||||
return NULL;
|
// the client, and mongoose should not send client any more data.
|
||||||
}
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
|
|
||||||
|
// List of options. Last element must be NULL.
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
const char *options[] = {"listening_ports", "8080", NULL};
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
// Prepare callbacks structure. We have only one callback, the rest are NULL.
|
||||||
getchar(); // Wait until user hits "enter"
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.begin_request = begin_request_handler;
|
||||||
|
|
||||||
|
// Start the web server.
|
||||||
|
ctx = mg_start(&callbacks, NULL, options);
|
||||||
|
|
||||||
|
// Wait until user hits "enter". Server is running in separate thread.
|
||||||
|
// Navigating to http://localhost:8080 will invoke begin_request_handler().
|
||||||
|
getchar();
|
||||||
|
|
||||||
|
// Stop the server.
|
||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -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\" />"
|
"<input type=\"submit\" />"
|
||||||
"</form></body></html>";
|
"</form></body></html>";
|
||||||
|
|
||||||
static void *callback(enum mg_event event,
|
static int begin_request_handler(struct mg_connection *conn) {
|
||||||
struct mg_connection *conn) {
|
|
||||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
|
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
|
||||||
|
int post_data_len;
|
||||||
|
|
||||||
if (event == MG_NEW_REQUEST) {
|
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||||
if (!strcmp(ri->uri, "/handle_post_request")) {
|
// User has submitted a form, show submitted data and a variable value
|
||||||
// User has submitted a form, show submitted data and a variable value
|
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||||
char post_data[1024],
|
|
||||||
input1[sizeof(post_data)], input2[sizeof(post_data)];
|
|
||||||
int post_data_len;
|
|
||||||
|
|
||||||
// Read POST data
|
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
|
||||||
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
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
|
// Send reply to the client, showing submitted form values.
|
||||||
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||||
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
|
"Content-Type: text/plain\r\n\r\n"
|
||||||
|
"Submitted data: [%.*s]\n"
|
||||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
"Submitted data length: %d bytes\n"
|
||||||
"Content-Type: text/plain\r\n\r\n"
|
"input_1: [%s]\n"
|
||||||
"Submitted data: [%.*s]\n"
|
"input_2: [%s]\n",
|
||||||
"Submitted data length: %d bytes\n"
|
post_data_len, post_data, post_data_len, input1, input2);
|
||||||
"input_1: [%s]\n"
|
|
||||||
"input_2: [%s]\n",
|
|
||||||
post_data_len, post_data, post_data_len, input1, input2);
|
|
||||||
} else {
|
|
||||||
// Show HTML form.
|
|
||||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
|
||||||
"Content-Length: %d\r\n"
|
|
||||||
"Content-Type: text/html\r\n\r\n%s",
|
|
||||||
(int) strlen(html_form), html_form);
|
|
||||||
}
|
|
||||||
// Mark as processed
|
|
||||||
return "";
|
|
||||||
} else {
|
} else {
|
||||||
return NULL;
|
// Show HTML form.
|
||||||
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||||
|
"Content-Length: %d\r\n"
|
||||||
|
"Content-Type: text/html\r\n\r\n%s",
|
||||||
|
(int) strlen(html_form), html_form);
|
||||||
}
|
}
|
||||||
|
return 1; // Mark request as processed
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
const char *options[] = {"listening_ports", "8080", NULL};
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.begin_request = begin_request_handler;
|
||||||
|
ctx = mg_start(&callbacks, NULL, options);
|
||||||
getchar(); // Wait until user hits "enter"
|
getchar(); // Wait until user hits "enter"
|
||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
|||||||
+28
-26
@@ -17,42 +17,44 @@ typedef __int64 int64_t;
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
static int begin_request_handler(struct mg_connection *conn) {
|
||||||
if (event == MG_NEW_REQUEST) {
|
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
||||||
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||||
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
mg_upload(conn, "/tmp");
|
||||||
mg_upload(conn, "/tmp");
|
} else {
|
||||||
} else {
|
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
||||||
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
static const char *html_form =
|
||||||
static const char *html_form =
|
"<html><body>Upload example."
|
||||||
"<html><body>Upload example."
|
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
" enctype=\"multipart/form-data\">"
|
||||||
" enctype=\"multipart/form-data\">"
|
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
"<input type=\"submit\" value=\"Upload\" />"
|
||||||
"<input type=\"submit\" value=\"Upload\" />"
|
"</form></body></html>";
|
||||||
"</form></body></html>";
|
|
||||||
|
|
||||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||||
"Content-Length: %d\r\n"
|
"Content-Length: %d\r\n"
|
||||||
"Content-Type: text/html\r\n\r\n%s",
|
"Content-Type: text/html\r\n\r\n%s",
|
||||||
(int) strlen(html_form), html_form);
|
(int) strlen(html_form), html_form);
|
||||||
}
|
|
||||||
// Mark as processed
|
|
||||||
return "";
|
|
||||||
} else if (event == MG_UPLOAD) {
|
|
||||||
mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
// Mark request as processed
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void upload_handler(struct mg_connection *conn, const char *path) {
|
||||||
|
mg_printf(conn, "Saved [%s]", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
const char *options[] = {"listening_ports", "8080", NULL};
|
const char *options[] = {"listening_ports", "8080", NULL};
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.begin_request = begin_request_handler;
|
||||||
|
callbacks.upload = upload_handler;
|
||||||
|
ctx = mg_start(&callbacks, NULL, options);
|
||||||
getchar(); // Wait until user hits "enter"
|
getchar(); // Wait until user hits "enter"
|
||||||
pause();
|
|
||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+20
-51
@@ -5,69 +5,38 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
static void websocket_ready_handler(struct mg_connection *conn) {
|
||||||
if (event == MG_WEBSOCKET_READY) {
|
static const char *message = "server ready";
|
||||||
unsigned char buf[40];
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
|
||||||
buf[0] = 0x81;
|
}
|
||||||
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
|
|
||||||
mg_write(conn, buf, 2 + buf[1]);
|
|
||||||
return ""; // MG_WEBSOCKET_READY return value is ignored
|
|
||||||
} else if (event == MG_WEBSOCKET_MESSAGE) {
|
|
||||||
unsigned char buf[200], reply[200];
|
|
||||||
int n, i, mask_len, xor, msg_len, len;
|
|
||||||
|
|
||||||
// Read message from the client.
|
// Arguments:
|
||||||
// Accept only small (<126 bytes) messages.
|
// flags: first byte of websocket frame, see websocket RFC,
|
||||||
len = 0;
|
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||||
msg_len = mask_len = 0;
|
// data, data_len: payload data. Mask, if any, is already applied.
|
||||||
for (;;) {
|
static int websocket_data_handler(struct mg_connection *conn, int flags,
|
||||||
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
|
char *data, size_t data_len) {
|
||||||
return ""; // Read error, close websocket
|
(void) flags; // Unused
|
||||||
}
|
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||||
len += n;
|
|
||||||
if (len >= 2) {
|
|
||||||
msg_len = buf[1] & 127;
|
|
||||||
mask_len = (buf[1] & 128) ? 4 : 0;
|
|
||||||
if (msg_len > 125) {
|
|
||||||
return ""; // Message is too long, close websocket
|
|
||||||
}
|
|
||||||
// If we've buffered the whole message, exit the loop
|
|
||||||
if (len >= 2 + mask_len + msg_len) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare frame
|
// Returning zero means stoping websocket conversation.
|
||||||
reply[0] = 0x81; // text, FIN set
|
// Close the conversation if client has sent us "exit" string.
|
||||||
reply[1] = msg_len;
|
return memcmp(data, "exit", 4);
|
||||||
|
|
||||||
// Copy message from request to reply, applying the mask if required.
|
|
||||||
for (i = 0; i < msg_len; i++) {
|
|
||||||
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
|
|
||||||
reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Echo the message back to the client
|
|
||||||
mg_write(conn, reply, 2 + msg_len);
|
|
||||||
|
|
||||||
// Return non-NULL means stoping websocket conversation.
|
|
||||||
// Close the conversation if client has sent us "exit" string.
|
|
||||||
return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL;
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
struct mg_context *ctx;
|
struct mg_context *ctx;
|
||||||
|
struct mg_callbacks callbacks;
|
||||||
const char *options[] = {
|
const char *options[] = {
|
||||||
"listening_ports", "8080",
|
"listening_ports", "8080",
|
||||||
"document_root", "websocket_html_root",
|
"document_root", "websocket_html_root",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx = mg_start(&callback, NULL, options);
|
memset(&callbacks, 0, sizeof(callbacks));
|
||||||
|
callbacks.websocket_ready = websocket_ready_handler;
|
||||||
|
callbacks.websocket_data = websocket_data_handler;
|
||||||
|
ctx = mg_start(&callbacks, NULL, options);
|
||||||
getchar(); // Wait until user hits "enter"
|
getchar(); // Wait until user hits "enter"
|
||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>WebSocket Test</title>
|
<title>WebSocket Test</title>
|
||||||
<script language="javascript" type="text/javascript">
|
<script language="javascript" type="text/javascript">
|
||||||
|
|
||||||
var writeToScreen = function(message) {
|
var writeToScreen = function(message) {
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
writeToScreen('<span style="color: red; ">ERROR: </span> ' + ev.data);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style> div {font: small Verdana; } </style>
|
<style> div {font: small Verdana; } </style>
|
||||||
<h2>Mongoose WebSocket Test</h2>
|
<h2>Mongoose WebSocket Test</h2>
|
||||||
|
|
||||||
<div style="width: 400px; color: #aaa; padding: 1em; ">
|
<div style="width: 400px; color: #aaa; padding: 1em; ">
|
||||||
This page code creates websocket to the URI "/foo",
|
This page code creates websocket to the URI "/foo",
|
||||||
@@ -40,5 +40,5 @@
|
|||||||
receiving the "exit" message.
|
receiving the "exit" message.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="output"></div>
|
<div id="output"></div>
|
||||||
</html>
|
</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 .
|
|
||||||
+1136
-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
|
// This library is dual-licensed: you can redistribute it and/or modify
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// it under the terms of the GNU General Public License version 2 as
|
||||||
// in the Software without restriction, including without limitation the rights
|
// published by the Free Software Foundation. For the terms of this
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
// license, see <http://www.gnu.org/licenses/>.
|
||||||
// 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
|
// You are free to use this library under the terms of the GNU General
|
||||||
// all copies or substantial portions of the Software.
|
// 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
|
// Alternatively, you can license this library under a commercial
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// license, as set out in <http://cesanta.com/products.html>.
|
||||||
// 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.
|
|
||||||
|
|
||||||
#ifndef MONGOOSE_HEADER_INCLUDED
|
#ifndef MONGOOSE_HEADER_INCLUDED
|
||||||
#define MONGOOSE_HEADER_INCLUDED
|
#define MONGOOSE_HEADER_INCLUDED
|
||||||
@@ -42,129 +39,115 @@ struct mg_request_info {
|
|||||||
long remote_ip; // Client's IP address
|
long remote_ip; // Client's IP address
|
||||||
int remote_port; // Client's port
|
int remote_port; // Client's port
|
||||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||||
int num_headers; // Number of headers
|
void *user_data; // User data pointer passed to mg_start()
|
||||||
|
void *conn_data; // Connection-specific, per-thread user data.
|
||||||
|
|
||||||
|
int num_headers; // Number of HTTP headers
|
||||||
struct mg_header {
|
struct mg_header {
|
||||||
const char *name; // HTTP header name
|
const char *name; // HTTP header name
|
||||||
const char *value; // HTTP header value
|
const char *value; // HTTP header value
|
||||||
} http_headers[64]; // Maximum 64 headers
|
} http_headers[64]; // Maximum 64 headers
|
||||||
void *user_data; // User data pointer passed to mg_start()
|
|
||||||
void *ev_data; // Event-specific data pointer
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Various events on which user-defined callback function is called by Mongoose.
|
// This structure needs to be passed to mg_start(), to let mongoose know
|
||||||
enum mg_event {
|
// which callbacks to invoke. For detailed description, see
|
||||||
// New HTTP request has arrived from the client.
|
// https://github.com/valenok/mongoose/blob/master/UserManual.md
|
||||||
// If callback returns non-NULL, Mongoose stops handling current request.
|
struct mg_callbacks {
|
||||||
// ev_data contains NULL.
|
// Called when mongoose has received new HTTP request.
|
||||||
MG_NEW_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.
|
// Called when mongoose has finished processing request.
|
||||||
// Callback return value is ignored.
|
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||||
// ev_data contains integer HTTP status code:
|
|
||||||
// int http_reply_status_code = (long) request_info->ev_data;
|
|
||||||
MG_REQUEST_COMPLETE,
|
|
||||||
|
|
||||||
// HTTP error must be returned to the client.
|
// Called when mongoose is about to log a message. If callback returns
|
||||||
// If callback returns non-NULL, Mongoose stops handling error.
|
// non-zero, mongoose does not log anything.
|
||||||
// ev_data contains HTTP error code:
|
int (*log_message)(const struct mg_connection *, const char *message);
|
||||||
// int http_reply_status_code = (long) request_info->ev_data;
|
|
||||||
MG_HTTP_ERROR,
|
|
||||||
|
|
||||||
// Mongoose logs a message.
|
// Called when mongoose initializes SSL library.
|
||||||
// If callback returns non-NULL, Mongoose stops handling that event.
|
int (*init_ssl)(void *ssl_context, void *user_data);
|
||||||
// ev_data contains a message to be logged:
|
|
||||||
// const char *log_message = request_info->ev_data;
|
|
||||||
MG_EVENT_LOG,
|
|
||||||
|
|
||||||
// SSL initialization, sent before certificate setup.
|
// Called when websocket request is received, before websocket handshake.
|
||||||
// If callback returns non-NULL, Mongoose does not set up certificates.
|
// If callback returns 0, mongoose proceeds with handshake, otherwise
|
||||||
// ev_data contains server's OpenSSL context:
|
// cinnection is closed immediately.
|
||||||
// SSL_CTX *ssl_context = request_info->ev_data;
|
int (*websocket_connect)(const struct mg_connection *);
|
||||||
MG_INIT_SSL,
|
|
||||||
|
|
||||||
// Sent on HTTP connect, before websocket handshake.
|
// Called when websocket handshake is successfully completed, and
|
||||||
// If user callback returns NULL, then mongoose proceeds
|
// connection is ready for data exchange.
|
||||||
// with handshake, otherwise it closes the connection.
|
void (*websocket_ready)(struct mg_connection *);
|
||||||
// ev_data contains NULL.
|
|
||||||
MG_WEBSOCKET_CONNECT,
|
|
||||||
|
|
||||||
// Handshake has been successfully completed.
|
// Called when data frame has been received from the client.
|
||||||
// Callback's return value is ignored.
|
// Parameters:
|
||||||
// ev_data contains NULL.
|
// bits: first byte of the websocket frame, see websocket RFC at
|
||||||
MG_WEBSOCKET_READY,
|
// 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().
|
// Called when mongoose tries to open a file. Used to intercept file open
|
||||||
// If user callback returns non-NULL, mongoose closes the websocket.
|
// calls, and serve file data from memory instead.
|
||||||
// ev_data contains NULL.
|
// Parameters:
|
||||||
MG_WEBSOCKET_MESSAGE,
|
// 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.
|
// Called when mongoose is about to serve Lua server page (.lp file), if
|
||||||
// Callback's return value is ignored.
|
// Lua support is enabled.
|
||||||
// ev_data contains NULL.
|
// Parameters:
|
||||||
MG_WEBSOCKET_CLOSE,
|
// lua_context: "lua_State *" pointer.
|
||||||
|
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||||
|
|
||||||
// Mongoose tries to open file.
|
// Called when mongoose has uploaded a file to a temporary directory as a
|
||||||
// If callback returns non-NULL, Mongoose will not try to open it, but
|
// result of mg_upload() call.
|
||||||
// will use the returned value as a pointer to the file data. This allows
|
// Parameters:
|
||||||
// for example to serve files from memory.
|
// file_file: full path name to the uploaded file.
|
||||||
// ev_data contains file path, including document root path.
|
void (*upload)(struct mg_connection *, const char *file_name);
|
||||||
// Upon return, ev_data should return file size, which should be a long int.
|
|
||||||
//
|
|
||||||
// const char *file_name = request_info->ev_data;
|
|
||||||
// if (strcmp(file_name, "foo.txt") == 0) {
|
|
||||||
// request_info->ev_data = (void *) (long) 4;
|
|
||||||
// return "data";
|
|
||||||
// }
|
|
||||||
// return NULL;
|
|
||||||
//
|
|
||||||
// Note that this even is sent multiple times during one request. Each
|
|
||||||
// time mongoose tries to open or stat the file, this event is sent, e.g.
|
|
||||||
// for opening .htpasswd file, stat-ting requested file, opening requested
|
|
||||||
// file, etc.
|
|
||||||
MG_OPEN_FILE,
|
|
||||||
|
|
||||||
// Mongoose initializes Lua server page. Sent only if Lua support is enabled.
|
// Called at the beginning of mongoose's thread execution in the context of
|
||||||
// Callback's return value is ignored.
|
// that thread. To be used to perform any extra per-thread initialization.
|
||||||
// ev_data contains lua_State pointer.
|
// Parameters:
|
||||||
MG_INIT_LUA,
|
// 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.
|
// Called when mongoose's thread is about to terminate.
|
||||||
// Callback's return value is ignored.
|
// Same as thread_start() callback, but called when thread is about to be
|
||||||
// ev_data contains NUL-terminated file name.
|
// destroyed. Used to cleanup the state initialized by thread_start().
|
||||||
MG_UPLOAD,
|
// 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.
|
// Start web server.
|
||||||
//
|
//
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// callback: user defined event handling function or NULL.
|
// callbacks: mg_callbacks structure with user-defined callbacks.
|
||||||
// options: NULL terminated list of option_name, option_value pairs that
|
// options: NULL terminated list of option_name, option_value pairs that
|
||||||
// specify Mongoose configuration parameters.
|
// specify Mongoose configuration parameters.
|
||||||
//
|
//
|
||||||
// Side-effects: on UNIX, ignores SIGCHLD and SIGPIPE signals. If custom
|
// Side-effects: on UNIX, ignores SIGPIPE signals. If custom
|
||||||
// processing is required for these, signal handlers must be set up
|
// processing is required SIGPIPE, signal handler must be set up
|
||||||
// after calling mg_start().
|
// 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:
|
// Example:
|
||||||
// const char *options[] = {
|
// 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);
|
// 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.
|
// for the list of valid option and their possible values.
|
||||||
//
|
//
|
||||||
// Return:
|
// Return:
|
||||||
// web server context, or NULL on error.
|
// web server context, or NULL on error.
|
||||||
struct mg_context *mg_start(mg_callback_t callback, void *user_data,
|
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
|
||||||
const char **options);
|
void *user_data,
|
||||||
|
const char **configuration_options);
|
||||||
|
|
||||||
|
|
||||||
// Stop the web server.
|
// Stop the web server.
|
||||||
@@ -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.
|
// 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.
|
// Array is NULL terminated.
|
||||||
const char **mg_get_valid_option_names(void);
|
const char **mg_get_valid_option_names(void);
|
||||||
|
|
||||||
@@ -232,20 +217,37 @@ struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
|||||||
// Return:
|
// Return:
|
||||||
// 0 when the connection has been closed
|
// 0 when the connection has been closed
|
||||||
// -1 on error
|
// -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);
|
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.
|
// Return:
|
||||||
// Below are the macros for enabling compiler-specific checks for
|
// 0 when the connection has been closed
|
||||||
// printf-like arguments.
|
// -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
|
#undef PRINTF_FORMAT_STRING
|
||||||
#if _MSC_VER >= 1400
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
||||||
#include <sal.h>
|
#include <sal.h>
|
||||||
#if _MSC_VER > 1400
|
#if defined(_MSC_VER) && _MSC_VER > 1400
|
||||||
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
#define PRINTF_FORMAT_STRING(s) _Printf_format_string_ s
|
||||||
#else
|
#else
|
||||||
#define PRINTF_FORMAT_STRING(s) __format_string s
|
#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)
|
#define PRINTF_ARGS(x, y)
|
||||||
#endif
|
#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 *,
|
int mg_printf(struct mg_connection *,
|
||||||
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
|
||||||
|
|
||||||
@@ -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.
|
// 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);
|
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 success, length of the decoded variable.
|
||||||
// On error:
|
// On error:
|
||||||
// -1 (variable not found).
|
// -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
|
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
||||||
// NULL or zero length.
|
// NULL or zero length.
|
||||||
@@ -310,38 +320,38 @@ int mg_get_var(const char *data, size_t data_len,
|
|||||||
// Return:
|
// Return:
|
||||||
// On success, value length.
|
// On success, value length.
|
||||||
// On error:
|
// On error:
|
||||||
// -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
|
// -1 (either "Cookie:" header is not present at all or the requested
|
||||||
// -2 (destination buffer is NULL, zero length or too small to hold the value).
|
// parameter is not found).
|
||||||
int mg_get_cookie(const struct mg_connection *,
|
// -2 (destination buffer is NULL, zero length or too small to hold the
|
||||||
const char *cookie_name, char *buf, size_t buf_len);
|
// 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:
|
// Return:
|
||||||
// On success, valid pointer to the new connection
|
// On success, valid pointer to the new connection, suitable for mg_read().
|
||||||
// On error, NULL
|
// On error, NULL. error_buffer contains error message.
|
||||||
struct mg_connection *mg_connect(struct mg_context *ctx,
|
// Example:
|
||||||
const char *host, int port, int use_ssl);
|
// char ebuf[100];
|
||||||
|
// struct mg_connection *conn;
|
||||||
|
// conn = mg_download("google.com", 80, 0, ebuf, sizeof(ebuf),
|
||||||
|
// "%s", "GET / HTTP/1.0\r\nHost: google.com\r\n\r\n");
|
||||||
|
struct mg_connection *mg_download(const char *host, int port, int use_ssl,
|
||||||
|
char *error_buffer, size_t error_buffer_size,
|
||||||
|
PRINTF_FORMAT_STRING(const char *request_fmt),
|
||||||
|
...) PRINTF_ARGS(6, 7);
|
||||||
|
|
||||||
|
|
||||||
// Close the connection opened by mg_connect().
|
// Close the connection opened by mg_download().
|
||||||
void mg_close_connection(struct mg_connection *conn);
|
void mg_close_connection(struct mg_connection *conn);
|
||||||
|
|
||||||
|
|
||||||
// Download given URL to a given file.
|
|
||||||
// url: URL to download
|
|
||||||
// path: file name where to save the data
|
|
||||||
// request_info: pointer to a structure that will hold parsed reply headers
|
|
||||||
// buf, bul_len: a buffer for the reply headers
|
|
||||||
// Return:
|
|
||||||
// On error, NULL
|
|
||||||
// On success, opened file stream to the downloaded contents. The stream
|
|
||||||
// is positioned to the end of the file. It is the user's responsibility
|
|
||||||
// to fclose() the opened file stream.
|
|
||||||
FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
|
|
||||||
char *buf, size_t buf_len, struct mg_request_info *request_info);
|
|
||||||
|
|
||||||
|
|
||||||
// File upload functionality. Each uploaded file gets saved into a temporary
|
// File upload functionality. Each uploaded file gets saved into a temporary
|
||||||
// file and MG_UPLOAD event is sent.
|
// file and MG_UPLOAD event is sent.
|
||||||
// Return number of uploaded files.
|
// Return number of uploaded files.
|
||||||
@@ -362,6 +372,14 @@ const char *mg_get_builtin_mime_type(const char *file_name);
|
|||||||
// Return Mongoose version.
|
// Return Mongoose version.
|
||||||
const char *mg_version(void);
|
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.
|
// MD5 hash given strings.
|
||||||
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
// Buffer 'buf' must be 33 bytes long. Varargs is a NULL terminated list of
|
||||||
@@ -369,7 +387,7 @@ const char *mg_version(void);
|
|||||||
// MD5 hash. Example:
|
// MD5 hash. Example:
|
||||||
// char buf[33];
|
// char buf[33];
|
||||||
// mg_md5(buf, "aa", "bb", NULL);
|
// mg_md5(buf, "aa", "bb", NULL);
|
||||||
void mg_md5(char buf[33], ...);
|
char *mg_md5(char buf[33], ...);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#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>
|
HTTP/1.0 200 OK
|
||||||
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
|
Content-Type: text/html
|
||||||
<?
|
|
||||||
|
|
||||||
function is_prime(n)
|
<html><body>
|
||||||
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>
|
<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>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="Makefile" -->
|
<!--#include file="../build/Makefile" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="/etc/passwd" -->
|
<!--#include abspath="/etc/passwd" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="c:\boot.ini" -->
|
<!--#include abspath="c:\boot.ini" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
<html><pre>
|
<html><pre>
|
||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include "embed.c" -->
|
<!--#include "unit_test.c" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
</pre></html>
|
</pre></html>
|
||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
ssi_begin
|
ssi_begin
|
||||||
<!--#include file="Makefile" -->
|
<!--#include file="../build/Makefile" -->
|
||||||
ssi_end
|
ssi_end
|
||||||
+37
-104
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use IO::Socket;
|
use IO::Socket;
|
||||||
use File::Path;
|
use File::Path;
|
||||||
|
use File::Basename;
|
||||||
|
use Cwd;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
#use diagnostics;
|
#use diagnostics;
|
||||||
@@ -16,12 +18,15 @@ my $num_requests;
|
|||||||
my $dir_separator = on_windows() ? '\\' : '/';
|
my $dir_separator = on_windows() ? '\\' : '/';
|
||||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
||||||
my $test_dir_uri = "test_dir";
|
my $test_dir_uri = "test_dir";
|
||||||
my $root = 'test';
|
my $root = '../test';
|
||||||
my $test_dir = $root . $dir_separator. $test_dir_uri;
|
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 $config = 'mongoose.conf';
|
||||||
my $exe = '.' . $dir_separator . 'mongoose';
|
my $exe_ext = on_windows() ? '.exe' : '';
|
||||||
my $embed_exe = '.' . $dir_separator . 'embed';
|
my $mongoose_exe = '.' . $dir_separator . 'mongoose' . $exe_ext;
|
||||||
my $unit_test_exe = '.' . $dir_separator . 'unit_test';
|
my $embed_exe = '.' . $dir_separator . 'embed' . $exe_ext;
|
||||||
|
my $unit_test_exe = '.' . $dir_separator . 'unit_test' . $exe_ext;
|
||||||
my $exit_code = 0;
|
my $exit_code = 0;
|
||||||
|
|
||||||
my @files_to_delete = ('debug.log', 'access.log', $config, "$root/a/put.txt",
|
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
|
# Send the request to the 127.0.0.1:$port and return the reply
|
||||||
sub req {
|
sub req {
|
||||||
my ($request, $inc, $timeout) = @_;
|
my ($request, $inc, $timeout) = @_;
|
||||||
my $sock = IO::Socket::INET->new(Proto=>"tcp",
|
my $sock = IO::Socket::INET->new(Proto => 6,
|
||||||
PeerAddr=>'127.0.0.1', PeerPort=>$port);
|
PeerAddr => '127.0.0.1', PeerPort => $port);
|
||||||
fail("Cannot connect: $!") unless $sock;
|
fail("Cannot connect to http://127.0.0.1:$port : $!") unless $sock;
|
||||||
$sock->autoflush(1);
|
$sock->autoflush(1);
|
||||||
foreach my $byte (split //, $request) {
|
foreach my $byte (split //, $request) {
|
||||||
last unless print $sock $byte;
|
last unless print $sock $byte;
|
||||||
@@ -97,7 +102,6 @@ sub spawn {
|
|||||||
if (on_windows()) {
|
if (on_windows()) {
|
||||||
my @args = split /\s+/, $cmdline;
|
my @args = split /\s+/, $cmdline;
|
||||||
my $executable = $args[0];
|
my $executable = $args[0];
|
||||||
$executable .= '.exe';
|
|
||||||
Win32::Spawn($executable, $cmdline, $pid);
|
Win32::Spawn($executable, $cmdline, $pid);
|
||||||
die "Cannot spawn @_: $!" unless $pid;
|
die "Cannot spawn @_: $!" unless $pid;
|
||||||
} else {
|
} 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') {
|
if (scalar(@ARGV) > 0 and $ARGV[0] eq 'unit') {
|
||||||
do_unit_test();
|
do_unit_test();
|
||||||
exit 0;
|
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.
|
# Make sure we load config file if no options are given.
|
||||||
# Command line options override config files settings
|
# Command line options override config files settings
|
||||||
write_file($config, "access_log_file access.log\nlistening_ports 12345\n");
|
write_file($config, "access_log_file access.log\n" .
|
||||||
spawn("$exe -p $port");
|
"document_root $root\n" .
|
||||||
o("GET /test/hello.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'Loading config file');
|
"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;
|
unlink $config;
|
||||||
kill_spawned_child();
|
kill_spawned_child();
|
||||||
|
|
||||||
# Spawn the server on port $port
|
# Spawn the server on port $port
|
||||||
my $cmd = "$exe ".
|
my $cmd = "$mongoose_exe ".
|
||||||
"-listening_ports $port ".
|
"-listening_ports 127.0.0.1:$port ".
|
||||||
"-access_log_file access.log ".
|
"-access_log_file access.log ".
|
||||||
"-error_log_file debug.log ".
|
"-error_log_file debug.log ".
|
||||||
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||||
'-put_delete_auth_file test/passfile ' .
|
"-put_delete_auth_file $abs_root/passfile " .
|
||||||
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
'-access_control_list -0.0.0.0/0,+127.0.0.1 ' .
|
||||||
"-document_root $root ".
|
"-document_root $root ".
|
||||||
"-hide_files_patterns **exploit.pl ".
|
"-hide_files_patterns **exploit.PL ".
|
||||||
"-enable_keep_alive yes ".
|
"-enable_keep_alive yes ".
|
||||||
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
"-url_rewrite_patterns /aiased=/etc/,/ta=$test_dir";
|
||||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
$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');
|
o("GET /a+.txt HTTP/1.0\n\n", 'HTTP/1.1 200 OK', 'URL-decoding, + in URI');
|
||||||
|
|
||||||
# Test HTTP version parsing
|
# Test HTTP version parsing
|
||||||
o("GET / HTTPX/1.0\r\n\r\n", '400 Bad Request', 'Bad HTTP Version', 0);
|
o("GET / HTTPX/1.0\r\n\r\n", '^HTTP/1.1 500', 'Bad HTTP Version', 0);
|
||||||
o("GET / HTTP/x.1\r\n\r\n", '505 HTTP', 'Bad HTTP maj Version');
|
o("GET / HTTP/x.1\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP maj Version', 0);
|
||||||
o("GET / HTTP/1.1z\r\n\r\n", '505 HTTP', 'Bad HTTP min Version');
|
o("GET / HTTP/1.1z\r\n\r\n", '^HTTP/1.1 505', 'Bad HTTP min Version', 0);
|
||||||
o("GET / HTTP/02.0\r\n\r\n", '505 HTTP version not supported',
|
o("GET / HTTP/02.0\r\n\r\n", '^HTTP/1.1 505', 'HTTP Version >1.1', 0);
|
||||||
'HTTP Version >1.1');
|
|
||||||
|
|
||||||
# File with leading single dot
|
# File with leading single dot
|
||||||
o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1');
|
o("GET /.leading.dot.txt HTTP/1.0\n\n", 'abc123', 'Leading dot 1');
|
||||||
@@ -248,8 +248,9 @@ write_file($path, read_file($root . $dir_separator . 'env.cgi'));
|
|||||||
chmod(0755, $path);
|
chmod(0755, $path);
|
||||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n", "Content-Type: text/html\r\n\r\n",
|
||||||
'index.cgi execution');
|
'index.cgi execution');
|
||||||
|
|
||||||
o("GET /$test_dir_uri/x/ HTTP/1.0\n\n",
|
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",
|
o("GET /ta/x/ HTTP/1.0\n\n", "SCRIPT_NAME=/ta/x/index.cgi",
|
||||||
'Aliases SCRIPT_NAME');
|
'Aliases SCRIPT_NAME');
|
||||||
o("GET /hello.txt HTTP/1.1\nConnection: close\n\n", 'Connection: close',
|
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";
|
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 /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",
|
o("GET /bad2.cgi HTTP/1.0\n\n", "HTTP/1.1 123 Please pass me to the client\r",
|
||||||
'CGI Status code text');
|
'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 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/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/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
|
# Check that CGI's current directory is set to script's directory
|
||||||
my $copy_cmd = on_windows() ? 'copy' : 'cp';
|
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 $abs_path = on_windows() ? 'ssi6.shtml' : 'ssi5.shtml';
|
||||||
my $word = on_windows() ? 'boot loader' : 'root';
|
my $word = on_windows() ? 'boot loader' : 'root';
|
||||||
o("GET /$abs_path HTTP/1.0\n\n",
|
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",
|
o("GET /ssi7.shtml HTTP/1.0\n\n",
|
||||||
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
'ssi_begin.+Unit test.+ssi_end', 'SSI #include "..."');
|
||||||
o("GET /ssi8.shtml HTTP/1.0\n\n",
|
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
|
# Manipulate the passwords file
|
||||||
my $path = 'test_htpasswd';
|
my $path = 'test_htpasswd';
|
||||||
unlink $path;
|
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");
|
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");
|
or fail("Cannot edit user in a passwd file");
|
||||||
my $content = read_file($path);
|
my $content = read_file($path);
|
||||||
$content =~ /^b:a:\w+$/gs or fail("Bad content of the passwd file");
|
$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();
|
do_PUT_test();
|
||||||
kill_spawned_child();
|
kill_spawned_child();
|
||||||
do_unit_test();
|
do_unit_test();
|
||||||
do_embedded_test();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub do_PUT_test {
|
sub do_PUT_test {
|
||||||
@@ -456,79 +460,8 @@ sub do_PUT_test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub do_unit_test {
|
sub do_unit_test {
|
||||||
my $cmd = "cc -g -W -Wall -o $unit_test_exe $root/unit_test.c -I. ".
|
my $target = on_windows() ? 'wi' : 'un';
|
||||||
"-pthread -DNO_SSL ";
|
system("make $target") == 0 or fail("Unit test failed!");
|
||||||
if (on_windows()) {
|
|
||||||
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
|
|
||||||
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
|
|
||||||
}
|
|
||||||
print $cmd, "\n";
|
|
||||||
system($cmd) == 0 or fail("Cannot compile unit test");
|
|
||||||
system($unit_test_exe) == 0 or fail("Unit test failed!");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub do_embedded_test {
|
|
||||||
my $cmd = "cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. ".
|
|
||||||
"-pthread -DNO_SSL -DLISTENING_PORT=\\\"$port\\\"";
|
|
||||||
if (on_windows()) {
|
|
||||||
$cmd = "cl $root/embed.c mongoose.c /I. /nologo /DNO_SSL ".
|
|
||||||
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
|
|
||||||
}
|
|
||||||
print $cmd, "\n";
|
|
||||||
system($cmd) == 0 or fail("Cannot compile embedded unit test");
|
|
||||||
|
|
||||||
spawn("./$embed_exe");
|
|
||||||
o("GET /test_get_header HTTP/1.0\nHost: blah\n\n",
|
|
||||||
'Value: \[blah\]', 'mg_get_header', 0);
|
|
||||||
o("GET /test_get_var?a=b&my_var=foo&c=d HTTP/1.0\n\n",
|
|
||||||
'Value: \[foo\]', 'mg_get_var 1', 0);
|
|
||||||
o("GET /test_get_var?my_var=foo&c=d HTTP/1.0\n\n",
|
|
||||||
'Value: \[foo\]', 'mg_get_var 2', 0);
|
|
||||||
o("GET /test_get_var?a=b&my_var=foo HTTP/1.0\n\n",
|
|
||||||
'Value: \[foo\]', 'mg_get_var 3', 0);
|
|
||||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
|
||||||
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 4', 0);
|
|
||||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 18\n\n".
|
|
||||||
"a=b&my_var=foo&c=d", 'Value: \[foo\]', 'mg_get_var 5', 0);
|
|
||||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 14\n\n".
|
|
||||||
"a=b&my_var=foo", 'Value: \[foo\]', 'mg_get_var 6', 0);
|
|
||||||
o("GET /test_get_var?a=one%2btwo&my_var=foo& HTTP/1.0\n\n",
|
|
||||||
'Value: \[foo\]', 'mg_get_var 7', 0);
|
|
||||||
o("GET /test_get_var?my_var=one%2btwo&b=two%2b HTTP/1.0\n\n",
|
|
||||||
'Value: \[one\+two\]', 'mg_get_var 8', 0);
|
|
||||||
|
|
||||||
# + in form data MUST be decoded to space
|
|
||||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
|
||||||
"my_var=b+c", 'Value: \[b c\]', 'mg_get_var 9', 0);
|
|
||||||
|
|
||||||
# Test that big POSTed vars are not truncated
|
|
||||||
my $my_var = 'x' x 64000;
|
|
||||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n".
|
|
||||||
"my_var=$my_var", 'Value size: \[64000\]', 'mg_get_var 10', 0);
|
|
||||||
|
|
||||||
# Other methods should also work
|
|
||||||
o("PUT /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
|
||||||
"my_var=foo", 'Value: \[foo\]', 'mg_get_var 11', 0);
|
|
||||||
|
|
||||||
o("POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n".
|
|
||||||
"Content-Length: 3\n\na=b",
|
|
||||||
'Method: \[POST\].URI: \[/test_get_request_info\].'.
|
|
||||||
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'.
|
|
||||||
'HTTP header \[Content-Length\]: \[3\].'.
|
|
||||||
'Query string: \[xx=yy\].'.
|
|
||||||
'Remote IP: \[\d+\].Remote port: \[\d+\].'.
|
|
||||||
'Remote user: \[\]'
|
|
||||||
, 'request_info', 0);
|
|
||||||
o("GET /not_exist HTTP/1.0\n\n", 'Error: \[404\]', '404 handler', 0);
|
|
||||||
o("bad request\n\n", 'Error: \[400\]', '* error handler', 0);
|
|
||||||
# o("GET /foo/secret HTTP/1.0\n\n",
|
|
||||||
# '401 Unauthorized', 'mg_protect_uri', 0);
|
|
||||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
|
|
||||||
# '401 Unauthorized', 'mg_protect_uri (bill)', 0);
|
|
||||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
|
|
||||||
# '200 OK', 'mg_protect_uri (joe)', 0);
|
|
||||||
|
|
||||||
kill_spawned_child();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print "SUCCESS! All tests passed.\n";
|
print "SUCCESS! All tests passed.\n";
|
||||||
+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_WEBSOCKET
|
||||||
|
#define USE_LUA
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#define __cdecl
|
||||||
|
#define USE_IPV6
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// USE_* definitions must be made before #include "mongoose.c" !
|
||||||
|
|
||||||
#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); \
|
printf("Fail on line %d: [%s]\n", line, str); \
|
||||||
abort(); \
|
s_failed_tests++; \
|
||||||
} while (0)
|
} 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;
|
struct mg_request_info ri;
|
||||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||||
char req2[] = "BLAH / 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 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 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(strcmp(ri.http_version, "1.1") == 0);
|
||||||
ASSERT(ri.num_headers == 0);
|
ASSERT(ri.num_headers == 0);
|
||||||
|
|
||||||
ASSERT(parse_http_request(req2, sizeof(req2), &ri) == -1);
|
ASSERT(parse_http_message(req2, sizeof(req2), &ri) == -1);
|
||||||
ASSERT(parse_http_request(req3, sizeof(req3), &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.
|
// 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(ri.num_headers == 3);
|
||||||
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
|
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
|
||||||
ASSERT(strcmp(ri.http_headers[0].value, "foo bar") == 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].name, "baz\r\n\r") == 0);
|
||||||
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
|
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
|
||||||
|
|
||||||
// TODO(lsm): add more tests.
|
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) {
|
static void test_should_keep_alive(void) {
|
||||||
@@ -46,7 +105,8 @@ static void test_should_keep_alive(void) {
|
|||||||
|
|
||||||
memset(&conn, 0, sizeof(conn));
|
memset(&conn, 0, sizeof(conn));
|
||||||
conn.ctx = &ctx;
|
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";
|
ctx.config[ENABLE_KEEP_ALIVE] = "no";
|
||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
@@ -58,13 +118,13 @@ static void test_should_keep_alive(void) {
|
|||||||
ASSERT(should_keep_alive(&conn) == 0);
|
ASSERT(should_keep_alive(&conn) == 0);
|
||||||
|
|
||||||
conn.must_close = 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);
|
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);
|
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);
|
ASSERT(should_keep_alive(&conn) == 1);
|
||||||
|
|
||||||
conn.status_code = 401;
|
conn.status_code = 401;
|
||||||
@@ -102,7 +162,8 @@ static void test_match_prefix(void) {
|
|||||||
ASSERT(match_prefix("*", 1, "/hello/") == 0);
|
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/") == -1);
|
||||||
ASSERT(match_prefix("**.a$|**.b$", 11, "/a/b.b") == 6);
|
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() {
|
static void test_remove_double_dots() {
|
||||||
@@ -121,106 +182,284 @@ static void test_remove_double_dots() {
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||||
printf("[%s] -> [%s]\n", data[i].before, data[i].after);
|
|
||||||
remove_double_dots_and_double_slashes(data[i].before);
|
remove_double_dots_and_double_slashes(data[i].before);
|
||||||
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
ASSERT(strcmp(data[i].before, data[i].after) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 *fetch_data = "hello world!\n";
|
||||||
static const char *inmemory_file_data = "hi there";
|
static const char *inmemory_file_data = "hi there";
|
||||||
static void *event_handler(enum mg_event event, struct mg_connection *conn) {
|
static const char *upload_filename = "upload_test.txt";
|
||||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
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")) {
|
static const char *open_file_cb(const struct mg_connection *conn,
|
||||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
const char *path, size_t *size) {
|
||||||
"Content-Length: %d\r\n"
|
(void) conn;
|
||||||
"Content-Type: text/plain\r\n\r\n"
|
if (!strcmp(path, "./blah")) {
|
||||||
"%s", (int) strlen(fetch_data), fetch_data);
|
*size = strlen(inmemory_file_data);
|
||||||
return "";
|
return inmemory_file_data;
|
||||||
} else if (event == MG_OPEN_FILE) {
|
|
||||||
const char *path = request_info->ev_data;
|
|
||||||
printf("%s: [%s]\n", __func__, path);
|
|
||||||
if (strcmp(path, "./blah") == 0) {
|
|
||||||
mg_get_request_info(conn)->ev_data =
|
|
||||||
(void *) (int) strlen(inmemory_file_data);
|
|
||||||
return (void *) inmemory_file_data;
|
|
||||||
}
|
|
||||||
} else if (event == MG_EVENT_LOG) {
|
|
||||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_mg_fetch(void) {
|
static void upload_cb(struct mg_connection *conn, const char *path) {
|
||||||
static const char *options[] = {
|
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||||
"document_root", ".",
|
char *p1, *p2;
|
||||||
"listening_ports", UNUSED_PORT,
|
int len1, len2;
|
||||||
NULL,
|
|
||||||
};
|
if (atoi(ri->query_string) == 1) {
|
||||||
char buf[2000], buf2[2000];
|
ASSERT(!strcmp(path, "./upload_test.txt"));
|
||||||
int n, length;
|
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_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_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
|
||||||
ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
|
ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
|
||||||
ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file,
|
ASSERT(mg_download("localhost", port, 1, ebuf, sizeof(ebuf),
|
||||||
buf, sizeof(buf), &ri) == NULL);
|
"%s", "") == NULL);
|
||||||
ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
|
|
||||||
buf, sizeof(buf), &ri) == NULL);
|
|
||||||
|
|
||||||
// Failed fetch, pass invalid file name
|
// Fetch nonexistent file, should see 404
|
||||||
ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
"/this/file/must/not/exist/ever",
|
"GET /gimbec HTTP/1.0\r\n\r\n")) != NULL);
|
||||||
buf, sizeof(buf), &ri) == NULL);
|
ASSERT(strcmp(conn->request_info.uri, "404") == 0);
|
||||||
|
mg_close_connection(conn);
|
||||||
|
|
||||||
// Successful fetch
|
ASSERT((conn = mg_download("google.com", 443, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
|
"GET / HTTP/1.0\r\n\r\n")) != NULL);
|
||||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
mg_close_connection(conn);
|
||||||
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);
|
|
||||||
|
|
||||||
// Fetch big file, mongoose.c
|
// Fetch main.c, should succeed
|
||||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c",
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
"GET /main.c HTTP/1.0\r\n\r\n")) != NULL);
|
||||||
ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
|
ASSERT(!strcmp(conn->request_info.uri, "200"));
|
||||||
ASSERT(file.size == ftell(fp));
|
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
||||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
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
|
// Fetch in-memory file, should succeed.
|
||||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah",
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
"GET /blah HTTP/1.1\r\n\r\n")) != NULL);
|
||||||
ASSERT(!mg_strcasecmp(ri.uri, "200"));
|
ASSERT((p1 = read_conn(conn, &len1)) != NULL);
|
||||||
n = ftell(fp);
|
ASSERT(len1 == (int) strlen(inmemory_file_data));
|
||||||
fseek(fp, 0, SEEK_SET);
|
ASSERT(memcmp(p1, inmemory_file_data, len1) == 0);
|
||||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
free(p1);
|
||||||
n = fread(buf2, 1, n, fp);
|
mg_close_connection(conn);
|
||||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
|
||||||
ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data));
|
// Fetch in-memory data with no Content-Length, should succeed.
|
||||||
ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp)));
|
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
|
||||||
fclose(fp);
|
"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);
|
mg_stop(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +471,6 @@ static void test_base64_encode(void) {
|
|||||||
|
|
||||||
for (i = 0; in[i] != NULL; i++) {
|
for (i = 0; in[i] != NULL; i++) {
|
||||||
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
base64_encode((unsigned char *) in[i], strlen(in[i]), buf);
|
||||||
printf("[%s] [%s]\n", out[i], buf);
|
|
||||||
ASSERT(!strcmp(buf, out[i]));
|
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];
|
char buf[100];
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
|
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
|
||||||
luaL_dostring(L, buf);
|
(void) luaL_dostring(L, buf);
|
||||||
lua_getglobal(L, var_name);
|
lua_getglobal(L, var_name);
|
||||||
v = lua_tostring(L, -1);
|
v = lua_tostring(L, -1);
|
||||||
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
|
|
||||||
ASSERT((value == NULL && v == NULL) ||
|
ASSERT((value == NULL && v == NULL) ||
|
||||||
(value != NULL && v != NULL && !strcmp(value, v)));
|
(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"
|
char http_request[] = "POST /foo/bar HTTP/1.1\r\n"
|
||||||
"Content-Length: 12\r\n"
|
"Content-Length: 12\r\n"
|
||||||
"Connection: close\r\n\r\nhello world!";
|
"Connection: close\r\n\r\nhello world!";
|
||||||
const char *page = "<? print('hi') ?>";
|
|
||||||
lua_State *L = luaL_newstate();
|
lua_State *L = luaL_newstate();
|
||||||
|
|
||||||
conn.ctx = &ctx;
|
conn.ctx = &ctx;
|
||||||
conn.buf = http_request;
|
conn.buf = http_request;
|
||||||
conn.buf_size = conn.data_len = strlen(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.request_info);
|
||||||
conn.content_len = conn.data_len - conn.request_len;
|
conn.content_len = conn.data_len - conn.request_len;
|
||||||
|
|
||||||
@@ -326,39 +562,22 @@ static void test_lua(void) {
|
|||||||
ASSERT(lua_gettop(L) == 0);
|
ASSERT(lua_gettop(L) == 0);
|
||||||
|
|
||||||
check_lua_expr(L, "'hi'", "hi");
|
check_lua_expr(L, "'hi'", "hi");
|
||||||
check_lua_expr(L, "request_info.request_method", "POST");
|
check_lua_expr(L, "mg.request_info.request_method", "POST");
|
||||||
check_lua_expr(L, "request_info.uri", "/foo/bar");
|
check_lua_expr(L, "mg.request_info.uri", "/foo/bar");
|
||||||
check_lua_expr(L, "request_info.num_headers", "2");
|
check_lua_expr(L, "mg.request_info.num_headers", "2");
|
||||||
check_lua_expr(L, "request_info.remote_ip", "0");
|
check_lua_expr(L, "mg.request_info.remote_ip", "0");
|
||||||
check_lua_expr(L, "request_info.http_headers['Content-Length']", "12");
|
check_lua_expr(L, "mg.request_info.http_headers['Content-Length']", "12");
|
||||||
check_lua_expr(L, "request_info.http_headers['Connection']", "close");
|
check_lua_expr(L, "mg.request_info.http_headers['Connection']", "close");
|
||||||
luaL_dostring(L, "post = read()");
|
(void) luaL_dostring(L, "post = mg.read()");
|
||||||
check_lua_expr(L, "# post", "12");
|
check_lua_expr(L, "# post", "12");
|
||||||
check_lua_expr(L, "post", "hello world!");
|
check_lua_expr(L, "post", "hello world!");
|
||||||
lua_close(L);
|
lua_close(L);
|
||||||
}
|
}
|
||||||
#endif
|
#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 void test_mg_stat(void) {
|
||||||
static struct mg_context ctx;
|
static struct mg_context ctx;
|
||||||
struct file file;
|
struct file file = STRUCT_FILE_INITIALIZER;
|
||||||
memset(&file, 'A', sizeof(file));
|
|
||||||
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
|
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,21 +599,189 @@ static void test_skip_quoted(void) {
|
|||||||
#endif
|
#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_base64_encode();
|
||||||
test_match_prefix();
|
test_match_prefix();
|
||||||
test_remove_double_dots();
|
test_remove_double_dots();
|
||||||
test_should_keep_alive();
|
test_should_keep_alive();
|
||||||
test_parse_http_request();
|
test_parse_http_message();
|
||||||
test_mg_fetch();
|
test_mg_download();
|
||||||
test_mg_get_var();
|
test_mg_get_var();
|
||||||
test_set_throttle();
|
test_set_throttle();
|
||||||
test_next_option();
|
test_next_option();
|
||||||
test_user_data();
|
|
||||||
test_mg_stat();
|
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
|
#ifdef USE_LUA
|
||||||
test_lua();
|
test_lua();
|
||||||
#endif
|
#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