mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eed09600f7 | ||
|
|
8d6634ff7e | ||
|
|
6c9ed3ca53 | ||
|
|
ede1befffa | ||
|
|
2cd4852321 | ||
|
|
1eb719f2d9 | ||
|
|
bf633082ac | ||
|
|
075a152779 | ||
|
|
c079a13b24 | ||
|
|
113c9d5a02 | ||
|
|
05fa677382 | ||
|
|
ab3c070e1c | ||
|
|
2e6a01b931 | ||
|
|
9fa66bf237 | ||
|
|
98c382e7af | ||
|
|
94c7a3862a | ||
|
|
6869b64b31 | ||
|
|
8faf6f7b72 | ||
|
|
99ddb2f12a | ||
|
|
ca9212c3ca | ||
|
|
c9183f3851 | ||
|
|
0912a7f473 | ||
|
|
38421657b3 | ||
|
|
64cfe1d389 | ||
|
|
69b0b58d46 | ||
|
|
0d442058e5 | ||
|
|
1ef31461bc | ||
|
|
0b09df97ac | ||
|
|
0263e39584 | ||
|
|
d8424726bb | ||
|
|
080cb2dd4b | ||
|
|
786caaecb2 | ||
|
|
eb92b5f1b3 | ||
|
|
c5ba6016aa | ||
|
|
73a845a327 | ||
|
|
6eec552ea4 | ||
|
|
cb62203cf3 | ||
|
|
4fcb64fe90 | ||
|
|
8966f47c7a | ||
|
|
e251375c0c | ||
|
|
672e1ec8c6 | ||
|
|
f8713ede2d | ||
|
|
0b5f854bf0 | ||
|
|
3770020b74 | ||
|
|
9f9a1638f7 | ||
|
|
fb07a937e7 | ||
|
|
025cd283c4 | ||
|
|
b39426dbf7 | ||
|
|
5157bd0960 | ||
|
|
2aaedef4af | ||
|
|
a0b037d288 | ||
|
|
011bdc22b2 | ||
|
|
b437d56736 | ||
|
|
6205e69ab2 | ||
|
|
038f8823f1 | ||
|
|
03dd8367bf | ||
|
|
b597da34da | ||
|
|
bf387f9544 | ||
|
|
365b5e8f8a | ||
|
|
47651766fb | ||
|
|
043ea54a2a | ||
|
|
55c8c82c4b | ||
|
|
557d1f4c49 | ||
|
|
2539a473fe | ||
|
|
6bd35a4c18 | ||
|
|
fb8f0ed4d6 | ||
|
|
3ffdf5459a | ||
|
|
02098b1903 | ||
|
|
c189553c8b | ||
|
|
d970705596 | ||
|
|
5c9ab40601 | ||
|
|
451d077170 | ||
|
|
30d1573346 | ||
|
|
0adef2db5b | ||
|
|
8c1866a4af | ||
|
|
2a7a594b49 | ||
|
|
78ea04556f | ||
|
|
7fe2944477 | ||
|
|
93131417e6 | ||
|
|
6ef5eb0665 | ||
|
|
24103ff5df | ||
|
|
2118de37c3 |
No files matched your search
@@ -4,7 +4,7 @@
|
||||
PROG= mongoose
|
||||
|
||||
all:
|
||||
@echo "make (linux|bsd|solaris|mac|windows|mingw)"
|
||||
@echo "make (linux|bsd|solaris|mac|windows|mingw|cygwin)"
|
||||
|
||||
# Possible COPT values: (in brackets are rough numbers for 'gcc -O2' on i386)
|
||||
# -DHAVE_MD5 - use system md5 library (-2kb)
|
||||
@@ -12,18 +12,23 @@ all:
|
||||
# -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
|
||||
# -DHAVE_STRTOUI64 - use system strtoui64() function for strtoull()
|
||||
# -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
|
||||
|
||||
GCC_WARNS = -W -Wall -pedantic
|
||||
# -Wno-missing-field-initializers -Wno-unused-parameter -Wno-format-zero-length -Wno-missing-braces
|
||||
CFLAGS = -std=c99 -O2 $(GCC_WARNS) $(COPT)
|
||||
MAC_SHARED = -flat_namespace -bundle -undefined suppress
|
||||
LINFLAGS = -ldl -pthread $(CFLAGS)
|
||||
@@ -64,50 +69,51 @@ 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
|
||||
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
|
||||
GUILIB= user32.lib shell32.lib
|
||||
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib
|
||||
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
|
||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
||||
/I $(CYA)/ctaocrypt/include /D _LIB /D OPENSSL_EXTRA
|
||||
|
||||
CYASRC= \
|
||||
$(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
|
||||
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) $(CYASRC) $(CYAFL) $(DEF)
|
||||
$(MSVC)/bin/lib *.obj /out:$@
|
||||
$(CL) /Fo$(CYA)/ $(CYA_SOURCES) $(CYAFL) $(DEF)
|
||||
$(MSVC)/bin/lib $(CYA)/*.obj /out:$@
|
||||
|
||||
windows: cyassl.lib
|
||||
lua.lib:
|
||||
$(CL) /c /Fo$(LUA)/ $(LUA_SOURCES)
|
||||
$(MSVC)/bin/lib $(LUA_SOURCES:%.c=%.obj) /out:$@
|
||||
|
||||
windows: cyassl.lib lua.lib
|
||||
$(MSVC)/bin/rc win32\res.rc
|
||||
$(CL) /I win32 main.c mongoose.c /GA $(LINK) win32\res.res \
|
||||
$(GUILIB) /out:$(PROG).exe
|
||||
@@ -125,6 +131,16 @@ mingw:
|
||||
$(CC) $(MINGWOPT) -Iwin32 mongoose.c main.c win32\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 ./win32/res.rc ./win32/res.o
|
||||
$(CC) $(CYGWINOPT) mongoose.c -lws2_32 \
|
||||
-shared -Wl,--out-implib=$(PROG).lib -o $(PROG).dll
|
||||
$(CC) $(CYGWINOPT) -Iwin32 mongoose.c main.c ./win32/res.o -lws2_32 \
|
||||
-ladvapi32 -o $(PROG).exe
|
||||
|
||||
##########################################################################
|
||||
### Manuals, cleanup, test, release
|
||||
@@ -144,4 +160,4 @@ release: clean
|
||||
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,bindings,examples,test,win32,mongoose.c,mongoose.h,mongoose.1,main.c} && mv x mongoose/$$F
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib
|
||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib win32/res.o
|
||||
@@ -3,7 +3,7 @@ Overview
|
||||
|
||||
Mongoose is easy to use web server. It also can be used as embedded web server library to provide web interface to applications.
|
||||
|
||||
Mongoose executable does not depend on any external library or configuration. If it is copied to any directory and launched from there, it starts to serve that directory on port 8080 (so to access files, go to http://localhost:8080). If some additional config is required - for example, different listening port or IP-based access control, then a mongoose.conf file with respective options can be created in the same directory where executable lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
|
||||
Mongoose executable does not depend on any external library or configuration. If it is copied to any directory and launched from there, it starts to serve that directory on port 8080 (so to access files, go to http://localhost:8080). If some additional config is required - for example, different listening port or IP-based access control, that can be done via command line flags or configuration file, which should be created in the same directory where mongoose itself lives. This makes Mongoose perfect for all sorts of demos, quick tests, file sharing, and Web programming.
|
||||
|
||||
|
||||
Features
|
||||
@@ -11,12 +11,15 @@ Features
|
||||
|
||||
- Crossplatform - works on Windows, MacOS and most flavors of UNIX
|
||||
- CGI, SSL, SSI, Digest (MD5) authorization, Websocket, WEbDAV support
|
||||
- Resumed download, URL rewrite support
|
||||
- IP-based ACL, Windows service, GET, POST, HEAD, PUT, DELETE methods
|
||||
- Small footprint: executable size is 40 kB on Linux 2.6 i386 system
|
||||
- Lua server pages (like PHP, but using Lua as a language instead of PHP)
|
||||
- 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
|
||||
- Small footprint: executable size is 50 kB on Linux 2.6 i386 system
|
||||
- 130 kilobytes Windows executable with all of the above compiled in, and no dependencies
|
||||
- Embeddable with simple and clean API ([mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)). The source is in single [mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file to make things easy.
|
||||
- Embedding examples: [hello.c](https://github.com/valenok/mongoose/blob/master/examples/hello.c), [post.c](https://github.com/valenok/mongoose/blob/master/examples/post.c), [upload.c](https://github.com/valenok/mongoose/blob/master/examples/upload.c), [websocket.c](https://github.com/valenok/mongoose/blob/master/examples/websocket.c)
|
||||
- Python and C# bindings
|
||||
- Super-liberal MIT license, great for commercial users!
|
||||
|
||||
|
||||
Mailing list
|
||||
@@ -29,3 +32,4 @@ Keep Sergey happy
|
||||
-----------------
|
||||
|
||||
I have a [books wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority) on Amazon. If you feel brave, you can buy me a book!
|
||||
Many thanks to all who already did so: T.Barmann, D.Hughes, J.C.Sloan and 4 others. Appreciated, guys, you keep my brains going!
|
||||
@@ -11,24 +11,24 @@ import sys
|
||||
def EventHandler(event, conn):
|
||||
info = conn.info
|
||||
if event == mongoose.HTTP_ERROR:
|
||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
||||
conn.printf('HTTP error: %d\n', conn.reply_status_code)
|
||||
conn.write('HTTP/1.0 200 OK\r\n')
|
||||
conn.write('Content-Type: text/plain\r\n\r\n')
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/show':
|
||||
conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
|
||||
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
|
||||
conn.printf('%s %s\n', info.request_method, info.uri)
|
||||
conn.write('HTTP/1.0 200 OK\r\n')
|
||||
conn.write('Content-Type: text/plain\r\n\r\n')
|
||||
conn.write('[%s] [%s] [%s]\n' % (info.request_method, info.uri,
|
||||
info.query_string))
|
||||
if info.request_method == 'POST':
|
||||
content_len = conn.get_header('Content-Length')
|
||||
post_data = conn.read(int(content_len))
|
||||
my_var = conn.get_var(post_data, 'my_var')
|
||||
else:
|
||||
my_var = conn.get_var(info.query_string, 'my_var')
|
||||
conn.printf('my_var: %s\n', my_var or '<not set>')
|
||||
conn.printf('HEADERS: \n')
|
||||
conn.write('my_var: %s\n' % (my_var or '<not set>'))
|
||||
conn.write('HEADERS: \n')
|
||||
for header in info.http_headers[:info.num_headers]:
|
||||
conn.printf(' %s: %s\n', header.name, header.value)
|
||||
conn.write(' %s: %s\n' % (header.name, header.value))
|
||||
return True
|
||||
elif event == mongoose.NEW_REQUEST and info.uri == '/form':
|
||||
conn.write('HTTP/1.0 200 OK\r\n'
|
||||
|
||||
@@ -49,6 +49,8 @@ WEBSOCKET_CONNECT = 5
|
||||
WEBSOCKET_READY = 6
|
||||
WEBSOCKET_MESSAGE = 7
|
||||
WEBSOCKET_CLOSE = 8
|
||||
OPEN_FILE = 9
|
||||
INIT_LUA = 10
|
||||
|
||||
|
||||
class mg_header(ctypes.Structure):
|
||||
@@ -72,6 +74,8 @@ class mg_request_info(ctypes.Structure):
|
||||
('is_ssl', ctypes.c_int),
|
||||
('num_headers', ctypes.c_int),
|
||||
('http_headers', mg_header * 64),
|
||||
('user_data', ctypes.c_void_p),
|
||||
('ev_data', ctypes.c_void_p),
|
||||
]
|
||||
|
||||
|
||||
@@ -86,10 +90,6 @@ class Connection(object):
|
||||
self.m = mongoose
|
||||
self.conn = ctypes.c_void_p(connection)
|
||||
self.info = self.m.dll.mg_get_request_info(self.conn).contents
|
||||
self.user_data = self.m.dll.mg_get_user_data(self.conn)
|
||||
self.log_message = self.m.dll.mg_get_log_message(self.conn)
|
||||
self.reply_status_code = self.m.dll.mg_get_reply_status_code(self.conn)
|
||||
self.ssl_context = self.m.dll.mg_get_ssl_context(self.conn)
|
||||
|
||||
def get_header(self, name):
|
||||
val = self.m.dll.mg_get_header(self.conn, name)
|
||||
@@ -137,10 +137,6 @@ class Mongoose(object):
|
||||
self.dll.mg_get_cookie.restype = ctypes.c_int
|
||||
self.dll.mg_get_option.restype = ctypes.c_char_p
|
||||
self.dll.mg_get_request_info.restype = ctypes.POINTER(mg_request_info)
|
||||
self.dll.mg_get_user_data.restype = ctypes.c_void_p
|
||||
self.dll.mg_get_reply_status_code.restype = ctypes.c_int
|
||||
self.dll.mg_get_log_message.restype = ctypes.c_char_p
|
||||
self.dll.mg_get_ssl_context.restype = ctypes.c_void_p
|
||||
|
||||
if callback:
|
||||
# Create a closure that will be called by the shared library.
|
||||
|
||||
@@ -8,3 +8,21 @@ all:
|
||||
$(CC) $(CFLAGS) post.c ../mongoose.c $$LIBS $(ADD) -o post;
|
||||
$(CC) $(CFLAGS) -DUSE_WEBSOCKET websocket.c ../mongoose.c $$LIBS $(ADD) -o websocket;
|
||||
$(CC) $(CFLAGS) chat.c ../mongoose.c $$LIBS $(ADD) -o chat
|
||||
|
||||
|
||||
MSVC = e:/vc6
|
||||
CL = $(MSVC)/bin/cl
|
||||
CLFLAGS = /MD /TC /nologo $(DBG) /W3 /DNO_SSL \
|
||||
/I$(MSVC)/include /I.. /Dsnprintf=_snprintf \
|
||||
/link /incremental:no /libpath:$(MSVC)/lib /machine:IX86
|
||||
|
||||
windows:
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) hello.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) upload.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) post.c ../mongoose.c $(CLFLAGS)
|
||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
||||
|
||||
clean:
|
||||
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
|
||||
@@ -346,6 +346,9 @@ static void *event_handler(enum mg_event event,
|
||||
// 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 {
|
||||
processed = NULL;
|
||||
}
|
||||
|
||||
+25
-96
@@ -5,107 +5,33 @@
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#define strtoll strtol
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <inttypes.h>
|
||||
#include <unistd.h>
|
||||
#endif // !_WIN32
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
// Make sure that form has enctype="multipart/form-data" attribute
|
||||
static const char *html_form =
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
static const char *HTTP_500 = "HTTP/1.0 500 Server Error\r\n\r\n";
|
||||
|
||||
static void handle_file_upload(struct mg_connection *conn) {
|
||||
const char *cl_header;
|
||||
char post_data[16 * 1024], path[999], file_name[1024], mime_type[100],
|
||||
buf[BUFSIZ], *eop, *s, *p;
|
||||
FILE *fp;
|
||||
long long int cl, written;
|
||||
int fd, n, post_data_len;
|
||||
|
||||
// Figure out total content length. Return if it is not present or invalid.
|
||||
cl_header = mg_get_header(conn, "Content-Length");
|
||||
if (cl_header == NULL || (cl = strtoll(cl_header, NULL, 10)) <= 0) {
|
||||
mg_printf(conn, "%s%s", HTTP_500, "Invalid Conent-Length");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the initial chunk into memory. This should be multipart POST data.
|
||||
// Parse headers, where we should find file name and content-type.
|
||||
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||
file_name[0] = mime_type[0] = '\0';
|
||||
for (s = p = post_data; p < &post_data[post_data_len]; p++) {
|
||||
if (p[0] == '\r' && p[1] == '\n') {
|
||||
if (s == p) {
|
||||
p += 2;
|
||||
break; // End of headers
|
||||
}
|
||||
p[0] = p[1] = '\0';
|
||||
sscanf(s, "Content-Type: %99s", mime_type);
|
||||
// TODO(lsm): don't expect filename to be the 3rd field,
|
||||
// parse the header properly instead.
|
||||
sscanf(s, "Content-Disposition: %*s %*s filename=\"%1023[^\"]",
|
||||
file_name);
|
||||
s = p + 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Finished parsing headers. Now "p" points to the first byte of data.
|
||||
// Calculate file size
|
||||
cl -= p - post_data; // Subtract headers size
|
||||
cl -= strlen(post_data); // Subtract the boundary marker at the end
|
||||
cl -= 6; // Subtract "\r\n" before and after boundary
|
||||
|
||||
// Construct destination file name. Write to /tmp, do not allow
|
||||
// paths that contain slashes.
|
||||
if ((s = strrchr(file_name, '/')) == NULL) {
|
||||
s = file_name;
|
||||
}
|
||||
snprintf(path, sizeof(path), "/tmp/%s", s);
|
||||
|
||||
if (file_name[0] == '\0') {
|
||||
mg_printf(conn, "%s%s", HTTP_500, "Can't get file name");
|
||||
} else if (cl <= 0) {
|
||||
mg_printf(conn, "%s%s", HTTP_500, "Empty file");
|
||||
} else if ((fd = open(path, O_CREAT | O_TRUNC |
|
||||
O_WRONLY | O_EXLOCK | O_CLOEXEC)) < 0) {
|
||||
// We're opening the file with exclusive lock held. This guarantee us that
|
||||
// there is no other thread can save into the same file simultaneously.
|
||||
mg_printf(conn, "%s%s", HTTP_500, "Cannot open file");
|
||||
} else if ((fp = fdopen(fd, "w")) == NULL) {
|
||||
mg_printf(conn, "%s%s", HTTP_500, "Cannot reopen file stream");
|
||||
close(fd);
|
||||
} else {
|
||||
// Success. Write data into the file.
|
||||
eop = post_data + post_data_len;
|
||||
n = p + cl > eop ? (int) (eop - p) : (int) cl;
|
||||
(void) fwrite(p, 1, n, fp);
|
||||
written = n;
|
||||
while (written < cl &&
|
||||
(n = mg_read(conn, buf, cl - written > (long long) sizeof(buf) ?
|
||||
sizeof(buf) : cl - written)) > 0) {
|
||||
(void) fwrite(buf, 1, n, fp);
|
||||
written += n;
|
||||
}
|
||||
(void) fclose(fp);
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n"
|
||||
"Saved to [%s], written %llu bytes", path, cl);
|
||||
}
|
||||
}
|
||||
|
||||
static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
|
||||
if (event == MG_NEW_REQUEST) {
|
||||
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||
handle_file_upload(conn);
|
||||
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
||||
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||
mg_upload(conn, "/tmp");
|
||||
} else {
|
||||
// Show HTML form.
|
||||
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
||||
static const char *html_form =
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
@@ -113,9 +39,11 @@ static void *callback(enum mg_event event, struct mg_connection *conn) {
|
||||
}
|
||||
// Mark as processed
|
||||
return "";
|
||||
} else {
|
||||
return NULL;
|
||||
} else if (event == MG_UPLOAD) {
|
||||
mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
@@ -124,6 +52,7 @@ int main(void) {
|
||||
|
||||
ctx = mg_start(&callback, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
pause();
|
||||
mg_stop(ctx);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -221,7 +221,7 @@ static void init_server_name(void) {
|
||||
|
||||
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) {
|
||||
if (ev == MG_EVENT_LOG) {
|
||||
printf("%s\n", mg_get_log_message(conn));
|
||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
||||
}
|
||||
|
||||
// Returning NULL marks request as not handled, signalling mongoose to
|
||||
|
||||
+761
-394
File diff suppressed because it is too large.
Load diff
+109
-39
@@ -34,35 +34,104 @@ struct mg_connection; // Handle for the individual connection
|
||||
|
||||
// This structure contains information about the HTTP request.
|
||||
struct mg_request_info {
|
||||
char *request_method; // "GET", "POST", etc
|
||||
char *uri; // URL-decoded URI
|
||||
char *http_version; // E.g. "1.0", "1.1"
|
||||
char *query_string; // URL part after '?' (not including '?') or NULL
|
||||
char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
int num_headers; // Number of headers
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
const char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
int num_headers; // Number of headers
|
||||
struct mg_header {
|
||||
char *name; // HTTP header name
|
||||
char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
void *user_data; // User data pointer passed to the mg_start()
|
||||
void *ev_data; // Event-specific data pointer
|
||||
};
|
||||
|
||||
|
||||
// Various events on which user-defined function is called by Mongoose.
|
||||
// Various events on which user-defined callback function is called by Mongoose.
|
||||
enum mg_event {
|
||||
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
||||
MG_REQUEST_COMPLETE, // Mongoose has finished handling the request
|
||||
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
||||
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
||||
MG_INIT_SSL, // SSL initialization, sent before certificate setup
|
||||
MG_WEBSOCKET_CONNECT, // Sent on HTTP connect, before websocket handshake.
|
||||
// If user callback returns NULL, then mongoose proceeds
|
||||
// with handshake, otherwise it closes the connection.
|
||||
MG_WEBSOCKET_READY, // Handshake has been successfully completed.
|
||||
MG_WEBSOCKET_MESSAGE, // Incoming message from the client
|
||||
MG_WEBSOCKET_CLOSE, // Client has closed the connection
|
||||
// New HTTP request has arrived from the client.
|
||||
// If callback returns non-NULL, Mongoose stops handling current request.
|
||||
// ev_data contains NULL.
|
||||
MG_NEW_REQUEST,
|
||||
|
||||
// Mongoose has finished handling the request.
|
||||
// Callback return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_REQUEST_COMPLETE,
|
||||
|
||||
// HTTP error must be returned to the client.
|
||||
// If callback returns non-NULL, Mongoose stops handling error.
|
||||
// ev_data contains HTTP error code:
|
||||
// int http_reply_status_code = (long) request_info->ev_data;
|
||||
MG_HTTP_ERROR,
|
||||
|
||||
// Mongoose logs a message.
|
||||
// If callback returns non-NULL, Mongoose stops handling that event.
|
||||
// ev_data contains a message to be logged:
|
||||
// const char *log_message = request_info->ev_data;
|
||||
MG_EVENT_LOG,
|
||||
|
||||
// SSL initialization, sent before certificate setup.
|
||||
// If callback returns non-NULL, Mongoose does not set up certificates.
|
||||
// ev_data contains server's OpenSSL context:
|
||||
// SSL_CTX *ssl_context = request_info->ev_data;
|
||||
MG_INIT_SSL,
|
||||
|
||||
// Sent on HTTP connect, before websocket handshake.
|
||||
// If user callback returns NULL, then mongoose proceeds
|
||||
// with handshake, otherwise it closes the connection.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_CONNECT,
|
||||
|
||||
// Handshake has been successfully completed.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_READY,
|
||||
|
||||
// Incoming message from the client, data could be read with mg_read().
|
||||
// If user callback returns non-NULL, mongoose closes the websocket.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_MESSAGE,
|
||||
|
||||
// Client has closed the connection.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NULL.
|
||||
MG_WEBSOCKET_CLOSE,
|
||||
|
||||
// Mongoose tries to open file.
|
||||
// If callback returns non-NULL, Mongoose will not try to open it, but
|
||||
// will use the returned value as a pointer to the file data. This allows
|
||||
// for example to serve files from memory.
|
||||
// ev_data contains file path, including document root path.
|
||||
// Upon return, ev_data should return file size, which should be a long int.
|
||||
//
|
||||
// const char *file_name = request_info->ev_data;
|
||||
// if (strcmp(file_name, "foo.txt") == 0) {
|
||||
// request_info->ev_data = (void *) (long) 4;
|
||||
// return "data";
|
||||
// }
|
||||
// return NULL;
|
||||
//
|
||||
// Note that this even is sent multiple times during one request. Each
|
||||
// time mongoose tries to open or stat the file, this event is sent, e.g.
|
||||
// for opening .htpasswd file, stat-ting requested file, opening requested
|
||||
// file, etc.
|
||||
MG_OPEN_FILE,
|
||||
|
||||
// Mongoose initializes Lua server page. Sent only if Lua support is enabled.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains lua_State pointer.
|
||||
MG_INIT_LUA,
|
||||
|
||||
// Mongoose has uploaded file to a temporary directory.
|
||||
// Callback's return value is ignored.
|
||||
// ev_data contains NUL-terminated file name.
|
||||
MG_UPLOAD,
|
||||
};
|
||||
|
||||
|
||||
@@ -155,12 +224,7 @@ int mg_modify_passwords_file(const char *passwords_file_name,
|
||||
|
||||
|
||||
// Return information associated with the request.
|
||||
// These functions always succeed.
|
||||
const struct mg_request_info *mg_get_request_info(const struct mg_connection *);
|
||||
void *mg_get_user_data(struct mg_connection *);
|
||||
const char *mg_get_log_message(const struct mg_connection *);
|
||||
int mg_get_reply_status_code(const struct mg_connection *);
|
||||
void *mg_get_ssl_context(const struct mg_connection *);
|
||||
struct mg_request_info *mg_get_request_info(struct mg_connection *);
|
||||
|
||||
|
||||
// Send data to the client.
|
||||
@@ -222,19 +286,19 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
|
||||
// or request_info.query_string.
|
||||
// data_len: length of the encoded data.
|
||||
// var_name: variable name to decode from the buffer
|
||||
// buf: destination buffer for the decoded variable
|
||||
// buf_len: length of the destination buffer
|
||||
// dst: destination buffer for the decoded variable
|
||||
// dst_len: length of the destination buffer
|
||||
//
|
||||
// Return:
|
||||
// On success, length of the decoded variable.
|
||||
// On error:
|
||||
// -1 (variable not found, or destination buffer is too small).
|
||||
// -2 (destination buffer is NULL or zero length).
|
||||
// -1 (variable not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the decoded variable).
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated if it is not
|
||||
// NULL or zero length. In case of failure, dst[0] == '\0'.
|
||||
// NULL or zero length.
|
||||
int mg_get_var(const char *data, size_t data_len,
|
||||
const char *var_name, char *buf, size_t buf_len);
|
||||
const char *var_name, char *dst, size_t dst_len);
|
||||
|
||||
// Fetch value of certain cookie variable into the destination buffer.
|
||||
//
|
||||
@@ -244,9 +308,9 @@ int mg_get_var(const char *data, size_t data_len,
|
||||
//
|
||||
// Return:
|
||||
// On success, value length.
|
||||
// On error, -1 (either "Cookie:" header is not present at all, or the
|
||||
// requested parameter is not found, or destination buffer is too small
|
||||
// to hold the value).
|
||||
// On error:
|
||||
// -1 (either "Cookie:" header is not present at all or the requested parameter is not found).
|
||||
// -2 (destination buffer is NULL, zero length or too small to hold the value).
|
||||
int mg_get_cookie(const struct mg_connection *,
|
||||
const char *cookie_name, char *buf, size_t buf_len);
|
||||
|
||||
@@ -277,6 +341,12 @@ FILE *mg_fetch(struct mg_context *ctx, const char *url, const char *path,
|
||||
char *buf, size_t buf_len, struct mg_request_info *request_info);
|
||||
|
||||
|
||||
// File upload functionality. Each uploaded file gets saved into a temporary
|
||||
// file and MG_UPLOAD event is sent.
|
||||
// Return number of uploaded files.
|
||||
int mg_upload(struct mg_connection *conn, const char *destination_dir);
|
||||
|
||||
|
||||
// Convenience function -- create detached thread.
|
||||
// Return: 0 on success, non-0 on error.
|
||||
typedef void * (*mg_thread_func_t)(void *);
|
||||
|
||||
+3
-3
@@ -119,10 +119,10 @@ static void test_get_request_info(struct mg_connection *conn,
|
||||
|
||||
static void test_error(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
(void) ri;
|
||||
int status = (int) ri->ev_data;
|
||||
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
||||
"Conntection: close\r\n\r\n", mg_get_reply_status_code(conn));
|
||||
mg_printf(conn, "Error: [%d]", mg_get_reply_status_code(conn));
|
||||
"Conntection: close\r\n\r\n", status);
|
||||
mg_printf(conn, "Error: [%d]", status);
|
||||
}
|
||||
|
||||
static void test_post(struct mg_connection *conn,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<html>
|
||||
<p>Prime numbers from 0 to 100, calculated by Lua:</p>
|
||||
<?
|
||||
|
||||
function is_prime(n)
|
||||
if n <= 0 then return false end
|
||||
if n <= 2 then return true end
|
||||
if (n % 2 == 0) then return false end
|
||||
for i = 3, n / 2, 2 do
|
||||
if (n % i == 0) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
for i = 1, 100 do
|
||||
if is_prime(i) then print('<span>' .. i .. '</span> ') 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>
|
||||
@@ -180,6 +180,9 @@ my $cmd = "$exe ".
|
||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
||||
spawn($cmd);
|
||||
|
||||
o("GET /hello.txt HTTP/1.1\nConnection: close\nRange: bytes=3-50\r\n\r\n",
|
||||
'Content-Length: 15\s', 'Range past the file end');
|
||||
|
||||
o("GET /hello.txt HTTP/1.1\n\n GET /hello.txt HTTP/1.0\n\n",
|
||||
'HTTP/1.1 200.+keep-alive.+HTTP/1.1 200.+close',
|
||||
'Request pipelining', 2);
|
||||
|
||||
+150
-19
@@ -7,6 +7,8 @@
|
||||
} while (0)
|
||||
#define ASSERT(expr) do { if (!(expr)) FATAL(#expr, __LINE__); } while (0)
|
||||
|
||||
#define UNUSED_PORT "33796"
|
||||
|
||||
static void test_parse_http_request() {
|
||||
struct mg_request_info ri;
|
||||
char req1[] = "GET / HTTP/1.1\r\n\r\n";
|
||||
@@ -31,7 +33,7 @@ static void test_parse_http_request() {
|
||||
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
|
||||
ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
|
||||
|
||||
// TODO(lsm): add more tests.
|
||||
// TODO(lsm): add more tests.
|
||||
}
|
||||
|
||||
static void test_should_keep_alive(void) {
|
||||
@@ -126,52 +128,61 @@ static void test_remove_double_dots() {
|
||||
}
|
||||
|
||||
static const char *fetch_data = "hello world!\n";
|
||||
static void *event_handler(enum mg_event event,
|
||||
struct mg_connection *conn) {
|
||||
static const char *inmemory_file_data = "hi there";
|
||||
static void *event_handler(enum mg_event event, struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
|
||||
if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) {
|
||||
mg_printf(conn, "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"%s", (int) strlen(fetch_data), fetch_data);
|
||||
return "";
|
||||
} else if (event == MG_OPEN_FILE) {
|
||||
const char *path = request_info->ev_data;
|
||||
printf("%s: [%s]\n", __func__, path);
|
||||
if (strcmp(path, "./blah") == 0) {
|
||||
mg_get_request_info(conn)->ev_data =
|
||||
(void *) (int) strlen(inmemory_file_data);
|
||||
return (void *) inmemory_file_data;
|
||||
}
|
||||
} else if (event == MG_EVENT_LOG) {
|
||||
printf("%s\n", mg_get_log_message(conn));
|
||||
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data);
|
||||
}
|
||||
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_mg_fetch(void) {
|
||||
static const char *options[] = {
|
||||
"document_root", ".",
|
||||
"listening_ports", "33796",
|
||||
"listening_ports", UNUSED_PORT,
|
||||
NULL,
|
||||
};
|
||||
char buf[2000], buf2[2000];
|
||||
int length;
|
||||
int n, length;
|
||||
struct mg_context *ctx;
|
||||
struct mg_request_info ri;
|
||||
const char *tmp_file = "temporary_file_name_for_unit_test.txt";
|
||||
struct mgstat st;
|
||||
struct file file;
|
||||
FILE *fp;
|
||||
|
||||
ASSERT((ctx = mg_start(event_handler, NULL, options)) != NULL);
|
||||
|
||||
// Failed fetch, pass invalid URL
|
||||
ASSERT(mg_fetch(ctx, "localhost", tmp_file, buf, sizeof(buf), &ri) == NULL);
|
||||
ASSERT(mg_fetch(ctx, "localhost:33796", tmp_file,
|
||||
ASSERT(mg_fetch(ctx, "localhost:" UNUSED_PORT, tmp_file,
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
ASSERT(mg_fetch(ctx, "http://$$$.$$$", tmp_file,
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
|
||||
// Failed fetch, pass invalid file name
|
||||
ASSERT(mg_fetch(ctx, "http://localhost:33796/data",
|
||||
ASSERT(mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
|
||||
"/this/file/must/not/exist/ever",
|
||||
buf, sizeof(buf), &ri) == NULL);
|
||||
|
||||
// Successful fetch
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/data",
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/data",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(ri.num_headers == 2);
|
||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
||||
@@ -184,12 +195,31 @@ static void test_mg_fetch(void) {
|
||||
fclose(fp);
|
||||
|
||||
// Fetch big file, mongoose.c
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:33796/mongoose.c",
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/mongoose.c",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(mg_stat("mongoose.c", &st) == 0);
|
||||
ASSERT(st.size == ftell(fp));
|
||||
ASSERT(mg_stat(fc(ctx), "mongoose.c", &file));
|
||||
ASSERT(file.size == ftell(fp));
|
||||
ASSERT(!strcmp(ri.request_method, "HTTP/1.1"));
|
||||
|
||||
// Fetch nonexistent file, /blah
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/boo",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(!mg_strcasecmp(ri.uri, "404"));
|
||||
fclose(fp);
|
||||
|
||||
// Fetch existing inmemory file
|
||||
ASSERT((fp = mg_fetch(ctx, "http://localhost:" UNUSED_PORT "/blah",
|
||||
tmp_file, buf, sizeof(buf), &ri)) != NULL);
|
||||
ASSERT(!mg_strcasecmp(ri.uri, "200"));
|
||||
n = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
||||
n = fread(buf2, 1, n, fp);
|
||||
printf("%s %d %d [%.*s]\n", __func__, n, (int) feof(fp), n, buf2);
|
||||
ASSERT((size_t) ftell(fp) == (size_t) strlen(inmemory_file_data));
|
||||
ASSERT(!memcmp(inmemory_file_data, buf2, ftell(fp)));
|
||||
fclose(fp);
|
||||
|
||||
remove(tmp_file);
|
||||
mg_stop(ctx);
|
||||
}
|
||||
@@ -208,23 +238,29 @@ static void test_base64_encode(void) {
|
||||
}
|
||||
|
||||
static void test_mg_get_var(void) {
|
||||
static const char *post[] = {"a=1&&b=2&d&=&c=3%20&e=", NULL};
|
||||
static const char *post[] = {
|
||||
"a=1&&b=2&d&=&c=3%20&e=",
|
||||
"q=&st=2012%2F11%2F13+17%3A05&et=&team_id=",
|
||||
NULL
|
||||
};
|
||||
char buf[20];
|
||||
|
||||
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "a", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '1' && buf[1] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "b", buf, sizeof(buf)) == 1);
|
||||
ASSERT(buf[0] == '2' && buf[1] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, sizeof(buf)) == 2);
|
||||
ASSERT(buf[0] == '3' && buf[1] == ' ' && buf[2] == '\0');
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "e", buf, sizeof(buf)) == 0);
|
||||
ASSERT(buf[0] == '\0');
|
||||
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -1);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "d", buf, sizeof(buf)) == -1);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "c", buf, 2) == -2);
|
||||
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", NULL, 10) == -2);
|
||||
ASSERT(mg_get_var(post[0], strlen(post[0]), "x", buf, 0) == -2);
|
||||
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 16) == -2);
|
||||
ASSERT(mg_get_var(post[1], strlen(post[1]), "st", buf, 17) == 16);
|
||||
}
|
||||
|
||||
static void test_set_throttle(void) {
|
||||
@@ -255,6 +291,95 @@ static void test_next_option(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_LUA
|
||||
static void check_lua_expr(lua_State *L, const char *expr, const char *value) {
|
||||
const char *v, *var_name = "myVar";
|
||||
char buf[100];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s = %s", var_name, expr);
|
||||
luaL_dostring(L, buf);
|
||||
lua_getglobal(L, var_name);
|
||||
v = lua_tostring(L, -1);
|
||||
printf("%s: %s: [%s] [%s]\n", __func__, expr, v == NULL ? "null" : v, value);
|
||||
ASSERT((value == NULL && v == NULL) ||
|
||||
(value != NULL && v != NULL && !strcmp(value, v)));
|
||||
}
|
||||
|
||||
static void test_lua(void) {
|
||||
static struct mg_connection conn;
|
||||
static struct mg_context ctx;
|
||||
|
||||
char http_request[] = "POST /foo/bar HTTP/1.1\r\n"
|
||||
"Content-Length: 12\r\n"
|
||||
"Connection: close\r\n\r\nhello world!";
|
||||
const char *page = "<? print('hi') ?>";
|
||||
lua_State *L = luaL_newstate();
|
||||
|
||||
conn.ctx = &ctx;
|
||||
conn.buf = http_request;
|
||||
conn.buf_size = conn.data_len = strlen(http_request);
|
||||
conn.request_len = parse_http_request(conn.buf, conn.data_len,
|
||||
&conn.request_info);
|
||||
conn.content_len = conn.data_len - conn.request_len;
|
||||
|
||||
prepare_lua_environment(&conn, L);
|
||||
ASSERT(lua_gettop(L) == 0);
|
||||
|
||||
check_lua_expr(L, "'hi'", "hi");
|
||||
check_lua_expr(L, "request_info.request_method", "POST");
|
||||
check_lua_expr(L, "request_info.uri", "/foo/bar");
|
||||
check_lua_expr(L, "request_info.num_headers", "2");
|
||||
check_lua_expr(L, "request_info.remote_ip", "0");
|
||||
check_lua_expr(L, "request_info.http_headers['Content-Length']", "12");
|
||||
check_lua_expr(L, "request_info.http_headers['Connection']", "close");
|
||||
luaL_dostring(L, "post = read()");
|
||||
check_lua_expr(L, "# post", "12");
|
||||
check_lua_expr(L, "post", "hello world!");
|
||||
lua_close(L);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *user_data_tester(enum mg_event event, struct mg_connection *conn) {
|
||||
struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
ASSERT(ri->user_data == (void *) 123);
|
||||
ASSERT(event == MG_NEW_REQUEST);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void test_user_data(void) {
|
||||
static const char *options[] = {"listening_ports", UNUSED_PORT, NULL};
|
||||
struct mg_context *ctx;
|
||||
|
||||
ASSERT((ctx = mg_start(user_data_tester, (void *) 123, options)) != NULL);
|
||||
ASSERT(ctx->user_data == (void *) 123);
|
||||
call_user(fc(ctx), MG_NEW_REQUEST);
|
||||
}
|
||||
|
||||
static void test_mg_stat(void) {
|
||||
static struct mg_context ctx;
|
||||
struct file file;
|
||||
memset(&file, 'A', sizeof(file));
|
||||
ASSERT(!mg_stat(fc(&ctx), " does not exist ", &file));
|
||||
}
|
||||
|
||||
static void test_skip_quoted(void) {
|
||||
char x[] = "a=1, b=2 c='hi \' there'", *s = x, *p;
|
||||
|
||||
p = skip_quoted(&s, ", ", ", ", 0);
|
||||
ASSERT(p != NULL && !strcmp(p, "a=1"));
|
||||
|
||||
p = skip_quoted(&s, ", ", ", ", 0);
|
||||
ASSERT(p != NULL && !strcmp(p, "b=2"));
|
||||
|
||||
// TODO(lsm): fix this
|
||||
#if 0
|
||||
p = skip_quoted(&s, "'", ", ", '\\');
|
||||
p = skip_quoted(&s, "'", ", ", '\\');
|
||||
printf("[%s]\n", p);
|
||||
ASSERT(p != NULL && !strcmp(p, "hi ' there"));
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_base64_encode();
|
||||
test_match_prefix();
|
||||
@@ -265,5 +390,11 @@ int main(void) {
|
||||
test_mg_get_var();
|
||||
test_set_throttle();
|
||||
test_next_option();
|
||||
test_user_data();
|
||||
test_mg_stat();
|
||||
#ifdef USE_LUA
|
||||
test_lua();
|
||||
#endif
|
||||
test_skip_quoted();
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user