mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08afdfa8b6 | ||
|
|
05f7d65fc9 | ||
|
|
a194e07545 | ||
|
|
dbb8a5bbf4 | ||
|
|
d3005df019 | ||
|
|
ef9b103a00 | ||
|
|
f0a2924a7c | ||
|
|
f109030cd0 | ||
|
|
eebdd06532 | ||
|
|
ca2fc71a01 | ||
|
|
e61e371c16 | ||
|
|
d5f07f5b9b | ||
|
|
0a8d5dde51 | ||
|
|
5646caa572 | ||
|
|
c7e5573d08 | ||
|
|
b9a226d109 | ||
|
|
fcf8b86288 | ||
|
|
a8b08c6784 | ||
|
|
1fb6f391a1 | ||
|
|
fcbc36ecde | ||
|
|
70771f29ce | ||
|
|
121391504f | ||
|
|
8455742440 | ||
|
|
5b786a5089 | ||
|
|
a6e74e7707 |
No files matched your search
@@ -36,20 +36,19 @@ LIB = lib$(PROG).so$(MONGOOSE_LIB_SUFFIX)
|
|||||||
# "-Wl,--as-needed" turned on by default in cc command.
|
# "-Wl,--as-needed" turned on by default in cc command.
|
||||||
# Also, this is turned in many other distros in static linkage builds.
|
# Also, this is turned in many other distros in static linkage builds.
|
||||||
linux:
|
linux:
|
||||||
$(CC) mongoose.c -shared -fPIC -fpic -o $(LIB) -Wl,-soname,$(LIB) -ldl -pthread $(CFLAGS)
|
|
||||||
$(CC) mongoose.c main.c -o $(PROG) -ldl -pthread $(CFLAGS)
|
$(CC) mongoose.c main.c -o $(PROG) -ldl -pthread $(CFLAGS)
|
||||||
|
|
||||||
bsd:
|
bsd:
|
||||||
$(CC) mongoose.c -shared -pthread -fpic -fPIC -o $(LIB) $(CFLAGS)
|
|
||||||
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
$(CC) mongoose.c main.c -pthread -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
mac:
|
mac:
|
||||||
$(CC) mongoose.c -pthread -o $(LIB) -flat_namespace -bundle -undefined suppress $(CFLAGS)
|
$(CC) mongoose.c main.c -pthread $(CFLAGS) -o $(PROG)
|
||||||
$(CC) mongoose.c main.c -DUSE_COCOA -pthread $(CFLAGS) -framework Cocoa -ObjC -arch i386 -arch x86_64 -o $(PROG)
|
|
||||||
V=`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`; DIR=dmg/$(PROG).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 $(PROG) $$DIR/Contents/MacOS/ && ln -fs /Applications dmg/ ; hdiutil create $(PROG)_$$V.dmg -volname "Mongoose $$V" -srcfolder dmg -ov #; rm -rf dmg
|
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:
|
solaris:
|
||||||
$(CC) mongoose.c -pthread -lnsl -lsocket -fpic -fPIC -shared -o $(LIB) $(CFLAGS)
|
|
||||||
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
$(CC) mongoose.c main.c -pthread -lnsl -lsocket -o $(PROG) $(CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
@@ -67,7 +66,7 @@ CYA = e:/cyassl-2.0.0rc2
|
|||||||
DBG = /DNDEBUG /O1
|
DBG = /DNDEBUG /O1
|
||||||
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
|
CL = $(MSVC)/bin/cl /MD /TC /nologo $(DBG) /Gz /W3 /DNO_SSL_DL \
|
||||||
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
|
/I$(MSVC)/include /DUSE_LUA /I$(LUA)
|
||||||
GUILIB= user32.lib shell32.lib
|
GUILIB= user32.lib shell32.lib comdlg32.lib
|
||||||
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
LINK = /link /incremental:no /libpath:$(MSVC)/lib /machine:IX86 \
|
||||||
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
|
/subsystem:windows ws2_32.lib advapi32.lib cyassl.lib lua.lib
|
||||||
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
CYAFL = /c /I $(CYA)/include -I $(CYA)/include/openssl /I$(MSVC)/INCLUDE \
|
||||||
@@ -114,7 +113,7 @@ windows: cyassl.lib lua.lib
|
|||||||
$(MSVC)/bin/rc build\res.rc
|
$(MSVC)/bin/rc build\res.rc
|
||||||
$(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \
|
$(CL) /Ibuild main.c mongoose.c /GA $(LINK) build\res.res \
|
||||||
$(GUILIB) /out:$(PROG).exe
|
$(GUILIB) /out:$(PROG).exe
|
||||||
$(CL) mongoose.c /GD $(LINK) /DLL /DEF:build\dll.def /out:$(PROG).dll
|
# $(CL) mongoose.c /GD $(LINK) /DLL /DEF:build\dll.def /out:$(PROG).dll
|
||||||
|
|
||||||
# Build for Windows under MinGW
|
# Build for Windows under MinGW
|
||||||
#MINGWDBG= -DDEBUG -O0 -ggdb
|
#MINGWDBG= -DDEBUG -O0 -ggdb
|
||||||
@@ -154,7 +153,9 @@ tests:
|
|||||||
perl test/test.pl $(TEST)
|
perl test/test.pl $(TEST)
|
||||||
|
|
||||||
release: clean
|
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,mongoose.c,mongoose.h,mongoose.1} && mv x mongoose/$$F
|
F=mongoose-`perl -lne '/define\s+MONGOOSE_VERSION\s+"(\S+)"/ and print $$1' mongoose.c`.tgz ; cd .. && tar -czf x mongoose/{LICENSE,Makefile,examples,test,build,*.[ch],*.md} && mv x mongoose/$$F
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf *.o *.core $(PROG) *.obj *.so $(PROG).txt *.dSYM *.tgz $(PROG).exe *.dll *.lib build/res.o build/res.RES
|
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
|
||||||
@@ -2,8 +2,9 @@ Project Mission
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
Project mission is to provide simple, functional, embeddable web server to
|
Project mission is to provide simple, functional, embeddable web server to
|
||||||
make it easy for application and device developers to implement web interface for their
|
make it easy for application and device developers to implement web interface
|
||||||
application and devices, and to offer a simple web development environment.
|
for their application and devices, and to offer a simple web development
|
||||||
|
environment.
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
--------
|
--------
|
||||||
@@ -11,32 +12,45 @@ Overview
|
|||||||
To accomplish it's mission, Mongoose keeps balance on functionality and
|
To accomplish it's mission, Mongoose keeps balance on 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)
|
- 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
|
||||||
- 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 [page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
- Lua server pages (PHP-like functionality using Lua), see
|
||||||
|
[page.lp](https://github.com/valenok/mongoose/blob/master/test/page.lp)
|
||||||
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
- Resumed download, URL rewrite, IP-based ACL, Windows service
|
||||||
- Excluding files from serving by URI pattern (file blacklist)
|
- 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
|
- 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
|
- 130 kilobytes Windows executable with all of the above and no dependencies
|
||||||
- Simple and clean embedding API ([mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)). The source is in single [mongoose.c](https://github.com/valenok/mongoose/blob/master/mongoose.c) file to make things easy.
|
- Simple and clean embedding API,
|
||||||
- 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)
|
[mongoose.h](https://github.com/valenok/mongoose/blob/master/mongoose.h)).
|
||||||
- Extensive documentation in form of [User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
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)
|
||||||
|
- Extensive documentation in form of
|
||||||
|
[User Manual](https://github.com/valenok/mongoose/blob/master/UserManual.md)
|
||||||
|
|
||||||
Questions can be asked at
|
Questions can be asked at
|
||||||
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users) mailing list.
|
[mongoose-users@google.com](http://groups.google.com/group/mongoose-users)
|
||||||
|
mailing list.
|
||||||
|
|
||||||
|
|
||||||
Keep Sergey happy
|
Keep Sergey happy
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a software engineer
|
Since 2004, Mongoose is being constantly improved by me, Sergey Lyubka, a
|
||||||
from Galway, Ireland. My other software I give to the community for free is
|
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).
|
[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
|
If you feel grateful for the stuff I've done, you can buy me a book from my
|
||||||
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks to all who
|
[Amazon wishlist](http://amzn.com/w/1OC2ZCPTQYIEP?sort=priority). Many thanks
|
||||||
already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo and 4 others.
|
to all who already did so: T.Barmann, D.Hughes, J.C.Sloan, R.Romeo,
|
||||||
Appreciated guys, you keep my brains going!
|
L.E.Spencer, and 4 others. Appreciated guys, you keep my brains going!
|
||||||
+133
-1
@@ -51,12 +51,144 @@ Usage Examples
|
|||||||
- How to share a Windows folder: copy mongoose executable to a folder and
|
- How to share a Windows folder: copy mongoose executable to a folder and
|
||||||
double-click the executable. The folder should be accessible via
|
double-click the executable. The folder should be accessible via
|
||||||
[http://localhost:8080](http://localhost:8080) in any browser.
|
[http://localhost:8080](http://localhost:8080) in any browser.
|
||||||
|
|
||||||
- How to start mongoose at UNIX startup time in daemon mode, serving
|
- How to start mongoose at UNIX startup time in daemon mode, serving
|
||||||
directory `/var/www`: put this line in the system startup script,
|
directory `/var/www`: put this line in the system startup script:
|
||||||
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
`/path/to/mongoose -listening_ports 80 -document_root /var/www &`
|
||||||
|
|
||||||
|
- How to serve user home directories using URL rewrite:
|
||||||
|
`mongoose -url_rewrite_patterns /~joe/=/home/joe/,/~bill=/home/bill/`
|
||||||
|
|
||||||
Command Line Options
|
Command Line Options
|
||||||
--------------------
|
--------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
-A htpasswd_file domain_name user_name password
|
||||||
|
Add/edit user's password in the passwords file. Deleting users
|
||||||
|
can be done with any text editor. Functionality is similar to
|
||||||
|
Apache's htdigest utility.
|
||||||
|
|
||||||
|
-C cgi_pattern
|
||||||
|
All files that fully match cgi_pattern are treated as CGI.
|
||||||
|
Default pattern allows CGI files be anywhere. To restrict CGIs to
|
||||||
|
certain directory, use e.g. "-C /cgi-bin/**.cgi". Default:
|
||||||
|
"**.cgi$|**.pl$|**.php$"
|
||||||
|
|
||||||
|
-E cgi_environment
|
||||||
|
Extra environment variables to be passed to the CGI script in
|
||||||
|
addition to standard ones. The list must be comma-separated list
|
||||||
|
of X=Y pairs, like this: "VARIABLE1=VALUE1,VARIABLE2=VALUE2".
|
||||||
|
Default: ""
|
||||||
|
|
||||||
|
-G put_delete_passwords_file
|
||||||
|
PUT and DELETE passwords file. This must be specified if PUT or
|
||||||
|
DELETE methods are used. Default: ""
|
||||||
|
|
||||||
|
-I cgi_interpreter
|
||||||
|
Use cgi_interpreter as a CGI interpreter for all CGI scripts
|
||||||
|
regardless script extension. Mongoose decides which interpreter
|
||||||
|
to use by looking at the first line of a CGI script. Default:
|
||||||
|
"".
|
||||||
|
|
||||||
|
-P protect_uri
|
||||||
|
Comma separated list of URI=PATH pairs, specifying that given
|
||||||
|
URIs must be protected with respected password files. Default: ""
|
||||||
|
|
||||||
|
-R authentication_domain
|
||||||
|
Authorization realm. Default: "mydomain.com"
|
||||||
|
|
||||||
|
-S ssi_pattern
|
||||||
|
All files that fully match ssi_pattern are treated as SSI.
|
||||||
|
Unknown SSI directives are silently ignored. Currently, two SSI
|
||||||
|
directives are supported, "include" and "exec". Default:
|
||||||
|
"**.shtml$|**.shtm$"
|
||||||
|
|
||||||
|
-T throttle
|
||||||
|
Limit download speed for clients. throttle is a comma-separated
|
||||||
|
list of key=value pairs, where key could be a '*' character
|
||||||
|
(limit for all connections), a subnet in form x.x.x.x/mask (limit
|
||||||
|
for a given subnet, for example 10.0.0.0/8), or an URI prefix
|
||||||
|
pattern (limit for the set of URIs, for example /foo/**). The
|
||||||
|
value is a floating-point number of bytes per second, optionally
|
||||||
|
followed by a `k' or `m' character, meaning kilobytes and
|
||||||
|
megabytes respectively. A limit of 0 means unlimited rate. The
|
||||||
|
last matching rule wins. For example, "*=1k,10.0.0.0/8" means
|
||||||
|
limit everybody to 1 kilobyte per second, but give people from
|
||||||
|
10/8 subnet unlimited speed. Default: ""
|
||||||
|
|
||||||
|
-a access_log_file
|
||||||
|
Access log file. Default: "", no logging is done.
|
||||||
|
|
||||||
|
-d enable_directory_listing
|
||||||
|
Enable/disable directory listing. Default: "yes"
|
||||||
|
|
||||||
|
-e error_log_file
|
||||||
|
Error log file. Default: "", no errors are logged.
|
||||||
|
|
||||||
|
-g global_passwords_file
|
||||||
|
Location of a global passwords file. If set, per-directory
|
||||||
|
.htpasswd files are ignored, and all requests must be authorised
|
||||||
|
against that file. Default: ""
|
||||||
|
|
||||||
|
-i index_files
|
||||||
|
Comma-separated list of files to be treated as directory index
|
||||||
|
files. Default: "index.html,index.htm,index.cgi"
|
||||||
|
|
||||||
|
-l access_control_list
|
||||||
|
Specify access control list (ACL). ACL is a comma separated list
|
||||||
|
of IP subnets, each subnet is prepended by '-' or '+' sign. Plus
|
||||||
|
means allow, minus means deny. If subnet mask is omitted, like
|
||||||
|
"-1.2.3.4", then it means single IP address. Mask may vary from 0
|
||||||
|
to 32 inclusive. On each request, full list is traversed, and
|
||||||
|
last match wins. Default setting is to allow all. For example, to
|
||||||
|
allow only 192.168/16 subnet to connect, run "mongoose
|
||||||
|
-0.0.0.0/0,+192.168/16". Default: ""
|
||||||
|
|
||||||
|
-m extra_mime_types
|
||||||
|
Extra mime types to recognize, in form "extension1=type1,exten-
|
||||||
|
sion2=type2,...". Extension must include dot. Example: "mongoose
|
||||||
|
-m .cpp=plain/text,.java=plain/text". Default: ""
|
||||||
|
|
||||||
|
-p listening_ports
|
||||||
|
Comma-separated list of ports to listen on. If the port is SSL, a
|
||||||
|
letter 's' must be appeneded, for example, "-p 80,443s" will open
|
||||||
|
port 80 and port 443, and connections on port 443 will be SSL-ed.
|
||||||
|
It is possible to specify an IP address to bind to. In this case,
|
||||||
|
an IP address and a colon must be prepended to the port number.
|
||||||
|
For example, to bind to a loopback interface on port 80 and to
|
||||||
|
all interfaces on HTTPS port 443, use "mongoose -p
|
||||||
|
127.0.0.1:80,443s". Default: "8080"
|
||||||
|
|
||||||
|
-r document_root
|
||||||
|
Location of the WWW root directory. Default: "."
|
||||||
|
|
||||||
|
-s ssl_certificate
|
||||||
|
Location of SSL certificate file. Default: ""
|
||||||
|
|
||||||
|
-t num_threads
|
||||||
|
Number of worker threads to start. Default: "10"
|
||||||
|
|
||||||
|
-u run_as_user
|
||||||
|
Switch to given user's credentials after startup. Default: ""
|
||||||
|
|
||||||
|
-w url_rewrite_patterns
|
||||||
|
Comma-separated list of URL rewrites in the form of "pattern=sub-
|
||||||
|
stitution,..." If the "pattern" matches some prefix of the
|
||||||
|
requested URL, then matched prefix gets substituted with "substi-
|
||||||
|
tution". For example, "-w /con-
|
||||||
|
fig=/etc,**.doc|**.rtf=/path/to/cgi-bin/handle_doc.cgi" will
|
||||||
|
serve all URLs that start with "/config" from the "/etc" direc-
|
||||||
|
tory, and call handle_doc.cgi script for .doc and .rtf file
|
||||||
|
requests. If some pattern matches, no further matching/substitu-
|
||||||
|
tion is performed (first matching pattern wins). Use full paths
|
||||||
|
in substitutions. Default: ""
|
||||||
|
|
||||||
|
-x hide_files_patterns
|
||||||
|
A prefix pattern for the files to hide. Files that match the pat-
|
||||||
|
tern will not show up in directory listing and return 404 Not
|
||||||
|
Found if requested. Default: ""
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
Common Problems
|
Common Problems
|
||||||
---------------
|
---------------
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
200 ICON DISCARDABLE "systray.ico"
|
100 ICON DISCARDABLE "systray.ico"
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 17 KiB |
+1
-1
@@ -25,4 +25,4 @@ windows:
|
|||||||
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
$(CL) /DUSE_WEBSOCKET websocket.c ../mongoose.c $(CLFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf hello hello.exe upload upload.exe post post.exe websocket websocket.exe chat chat.exe *.dSYM *.obj
|
rm -rf hello upload post websocket chat *.exe *.dSYM *.obj
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2004-2011 Sergey Lyubka
|
// Copyright (c) 2004-2013 Sergey Lyubka
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -40,6 +40,7 @@
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winsvc.h>
|
#include <winsvc.h>
|
||||||
|
#include <shlobj.h>
|
||||||
#define PATH_MAX MAX_PATH
|
#define PATH_MAX MAX_PATH
|
||||||
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
#define S_ISDIR(x) ((x) & _S_IFDIR)
|
||||||
#define DIRSEP '\\'
|
#define DIRSEP '\\'
|
||||||
@@ -110,6 +111,53 @@ static void show_usage_and_exit(void) {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(USE_COCOA)
|
||||||
|
static const char *config_file_top_comment =
|
||||||
|
"# Mongoose web server configuration file.\n"
|
||||||
|
"# For detailed description of every option, visit\n"
|
||||||
|
"# https://github.com/valenok/mongoose/blob/master/UserManual.md\n"
|
||||||
|
"# Lines starting with '#' and empty lines are ignored.\n"
|
||||||
|
"# To make a change, remove leading '#', modify option's value,\n"
|
||||||
|
"# save this file and then restart Mongoose.\n\n";
|
||||||
|
|
||||||
|
static const char *get_url_to_first_open_port(const struct mg_context *ctx) {
|
||||||
|
static char url[100];
|
||||||
|
const char *open_ports = mg_get_option(ctx, "listening_ports");
|
||||||
|
int a, b, c, d, port, n;
|
||||||
|
|
||||||
|
if (sscanf(open_ports, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &n) == 5) {
|
||||||
|
snprintf(url, sizeof(url), "%s://%d.%d.%d.%d:%d",
|
||||||
|
open_ports[n] == 's' ? "https" : "http", a, b, c, d, port);
|
||||||
|
} else if (sscanf(open_ports, "%d%n", &port, &n) == 1) {
|
||||||
|
snprintf(url, sizeof(url), "%s://localhost:%d",
|
||||||
|
open_ports[n] == 's' ? "https" : "http", port);
|
||||||
|
} else {
|
||||||
|
snprintf(url, sizeof(url), "%s", "http://localhost:8080");
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_config_file(const char *path) {
|
||||||
|
const char **names, *value;
|
||||||
|
FILE *fp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Create config file if it is not present yet
|
||||||
|
if ((fp = fopen(path, "r")) != NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
} else if ((fp = fopen(path, "a+")) != NULL) {
|
||||||
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
|
names = mg_get_valid_option_names();
|
||||||
|
for (i = 0; names[i] != NULL; i += 3) {
|
||||||
|
value = mg_get_option(ctx, names[i]);
|
||||||
|
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void verify_document_root(const char *root) {
|
static void verify_document_root(const char *root) {
|
||||||
const char *p, *path;
|
const char *p, *path;
|
||||||
char buf[PATH_MAX];
|
char buf[PATH_MAX];
|
||||||
@@ -188,18 +236,20 @@ static void process_command_line_arguments(char *argv[], char **options) {
|
|||||||
|
|
||||||
// Loop over the lines in config file
|
// Loop over the lines in config file
|
||||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
|
||||||
line_no++;
|
line_no++;
|
||||||
|
|
||||||
// Ignore empty lines and comments
|
// Ignore empty lines and comments
|
||||||
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
for (i = 0; isspace(* (unsigned char *) &line[i]); ) i++;
|
||||||
if (line[i] == '#' || line[i] == '\0')
|
if (line[i] == '#' || line[i] == '\0') {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||||
die("%s: line %d is invalid", config_file, (int) line_no);
|
printf("%s: line %d is invalid, ignoring it:\n %s",
|
||||||
|
config_file, (int) line_no, line);
|
||||||
|
} else {
|
||||||
|
set_option(options, opt, val);
|
||||||
}
|
}
|
||||||
set_option(options, opt, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) fclose(fp);
|
(void) fclose(fp);
|
||||||
@@ -266,9 +316,25 @@ static void start_mongoose(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
enum {
|
||||||
|
ID_ICON = 100, ID_QUIT, ID_SETTINGS, ID_SEPARATOR, ID_INSTALL_SERVICE,
|
||||||
|
ID_REMOVE_SERVICE, ID_STATIC, ID_GROUP, ID_SAVE, ID_RESET_DEFAULTS,
|
||||||
|
ID_STATUS, ID_CONNECT,
|
||||||
|
|
||||||
|
// All dynamically created text boxes for options have IDs starting from
|
||||||
|
// ID_CONTROLS, incremented by one.
|
||||||
|
ID_CONTROLS = 200,
|
||||||
|
|
||||||
|
// Text boxes for files have "..." buttons to open file browser. These
|
||||||
|
// buttons have IDs that are ID_FILE_BUTTONS_DELTA higher than associated
|
||||||
|
// text box ID.
|
||||||
|
ID_FILE_BUTTONS_DELTA = 1000
|
||||||
|
};
|
||||||
|
static HICON hIcon;
|
||||||
static SERVICE_STATUS ss;
|
static SERVICE_STATUS ss;
|
||||||
static SERVICE_STATUS_HANDLE hStatus;
|
static SERVICE_STATUS_HANDLE hStatus;
|
||||||
static const char *service_magic_argument = "--";
|
static const char *service_magic_argument = "--";
|
||||||
|
static NOTIFYICONDATA TrayIcon;
|
||||||
|
|
||||||
static void WINAPI ControlHandler(DWORD code) {
|
static void WINAPI ControlHandler(DWORD code) {
|
||||||
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
if (code == SERVICE_CONTROL_STOP || code == SERVICE_CONTROL_SHUTDOWN) {
|
||||||
@@ -296,41 +362,6 @@ static void WINAPI ServiceMain(void) {
|
|||||||
SetServiceStatus(hStatus, &ss);
|
SetServiceStatus(hStatus, &ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ID_TRAYICON 100
|
|
||||||
#define ID_QUIT 101
|
|
||||||
#define ID_EDIT_CONFIG 102
|
|
||||||
#define ID_SEPARATOR 103
|
|
||||||
#define ID_INSTALL_SERVICE 104
|
|
||||||
#define ID_REMOVE_SERVICE 105
|
|
||||||
#define ID_ICON 200
|
|
||||||
static NOTIFYICONDATA TrayIcon;
|
|
||||||
|
|
||||||
static void edit_config_file(void) {
|
|
||||||
const char **names, *value;
|
|
||||||
FILE *fp;
|
|
||||||
int i;
|
|
||||||
char cmd[200];
|
|
||||||
|
|
||||||
// Create config file if it is not present yet
|
|
||||||
if ((fp = fopen(config_file, "r")) != NULL) {
|
|
||||||
fclose(fp);
|
|
||||||
} else if ((fp = fopen(config_file, "a+")) != NULL) {
|
|
||||||
fprintf(fp,
|
|
||||||
"# Mongoose web server configuration file.\n"
|
|
||||||
"# Lines starting with '#' and empty lines are ignored.\n"
|
|
||||||
"# For detailed description of every option, visit\n"
|
|
||||||
"# http://code.google.com/p/mongoose/wiki/MongooseManual\n\n");
|
|
||||||
names = mg_get_valid_option_names();
|
|
||||||
for (i = 0; names[i] != NULL; i += 3) {
|
|
||||||
value = mg_get_option(ctx, names[i]);
|
|
||||||
fprintf(fp, "# %s %s\n", names[i + 1], *value ? value : "<value>");
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(cmd, sizeof(cmd), "notepad.exe %s", config_file);
|
|
||||||
WinExec(cmd, SW_SHOW);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void show_error(void) {
|
static void show_error(void) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
@@ -341,6 +372,272 @@ static void show_error(void) {
|
|||||||
MessageBox(NULL, buf, "Error", MB_OK);
|
MessageBox(NULL, buf, "Error", MB_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *align(void *ptr, DWORD alig) {
|
||||||
|
ULONG ul = (ULONG) ptr;
|
||||||
|
ul += alig;
|
||||||
|
ul &= ~alig;
|
||||||
|
return ((void *) ul);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_boolean_option(const char *option_name) {
|
||||||
|
return !strcmp(option_name, "enable_directory_listing") ||
|
||||||
|
!strcmp(option_name, "enable_keep_alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_filename_option(const char *option_name) {
|
||||||
|
return !strcmp(option_name, "cgi_interpreter") ||
|
||||||
|
!strcmp(option_name, "global_auth_file") ||
|
||||||
|
!strcmp(option_name, "put_delete_auth_file") ||
|
||||||
|
!strcmp(option_name, "access_log_file") ||
|
||||||
|
!strcmp(option_name, "error_log_file") ||
|
||||||
|
!strcmp(option_name, "ssl_certificate");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_directory_option(const char *option_name) {
|
||||||
|
return !strcmp(option_name, "document_root");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_numeric_options(const char *option_name) {
|
||||||
|
return !strcmp(option_name, "num_threads");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void save_config(HWND hDlg, FILE *fp) {
|
||||||
|
char value[2000];
|
||||||
|
const char **options, *name, *default_value;
|
||||||
|
int i, id;
|
||||||
|
|
||||||
|
fprintf(fp, "%s", config_file_top_comment);
|
||||||
|
options = mg_get_valid_option_names();
|
||||||
|
for (i = 0; options[i] != NULL; i += 3) {
|
||||||
|
name = options[i + 1];
|
||||||
|
id = ID_CONTROLS + i / 3;
|
||||||
|
if (is_boolean_option(name)) {
|
||||||
|
snprintf(value, sizeof(value), "%s",
|
||||||
|
IsDlgButtonChecked(hDlg, id) ? "yes" : "no");
|
||||||
|
} else {
|
||||||
|
GetDlgItemText(hDlg, id, value, sizeof(value));
|
||||||
|
}
|
||||||
|
default_value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||||
|
// If value is the same as default, skip it
|
||||||
|
if (strcmp(value, default_value) != 0) {
|
||||||
|
fprintf(fp, "%s %s\n", name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lP) {
|
||||||
|
FILE *fp;
|
||||||
|
int i;
|
||||||
|
const char *name, *value, **options = mg_get_valid_option_names();
|
||||||
|
|
||||||
|
switch (msg) {
|
||||||
|
case WM_CLOSE:
|
||||||
|
DestroyWindow(hDlg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_COMMAND:
|
||||||
|
switch (LOWORD(wParam)) {
|
||||||
|
case ID_SAVE:
|
||||||
|
EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
|
||||||
|
if ((fp = fopen(config_file, "w+")) != NULL) {
|
||||||
|
save_config(hDlg, fp);
|
||||||
|
fclose(fp);
|
||||||
|
mg_stop(ctx);
|
||||||
|
start_mongoose(__argc, __argv);
|
||||||
|
}
|
||||||
|
EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
|
||||||
|
break;
|
||||||
|
case ID_RESET_DEFAULTS:
|
||||||
|
for (i = 0; options[i] != NULL; i += 3) {
|
||||||
|
name = options[i + 1];
|
||||||
|
value = options[i + 2] == NULL ? "" : options[i + 2];
|
||||||
|
if (is_boolean_option(name)) {
|
||||||
|
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||||
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
|
} else {
|
||||||
|
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; options[i] != NULL; i += 3) {
|
||||||
|
name = options[i + 1];
|
||||||
|
if ((is_filename_option(name) || is_directory_option(name)) &&
|
||||||
|
LOWORD(wParam) == ID_CONTROLS + i / 3 + ID_FILE_BUTTONS_DELTA) {
|
||||||
|
OPENFILENAME of;
|
||||||
|
BROWSEINFO bi;
|
||||||
|
char path[PATH_MAX] = "";
|
||||||
|
|
||||||
|
memset(&of, 0, sizeof(of));
|
||||||
|
of.lStructSize = sizeof(of);
|
||||||
|
of.hwndOwner = (HWND) hDlg;
|
||||||
|
of.lpstrFile = path;
|
||||||
|
of.nMaxFile = sizeof(path);
|
||||||
|
of.lpstrInitialDir = mg_get_option(ctx, "document_root");
|
||||||
|
of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
|
||||||
|
|
||||||
|
memset(&bi, 0, sizeof(bi));
|
||||||
|
bi.hwndOwner = (HWND) hDlg;
|
||||||
|
bi.lpszTitle = "Choose WWW root directory:";
|
||||||
|
bi.ulFlags = BIF_RETURNONLYFSDIRS;
|
||||||
|
|
||||||
|
if (is_directory_option(name)) {
|
||||||
|
SHGetPathFromIDList(SHBrowseForFolder(&bi), path);
|
||||||
|
} else {
|
||||||
|
GetOpenFileName(&of);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path[0] != '\0') {
|
||||||
|
SetWindowText(GetDlgItem(hDlg, ID_CONTROLS + i / 3), path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WM_INITDIALOG:
|
||||||
|
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_SMALL, (LPARAM) hIcon);
|
||||||
|
SendMessage(hDlg, WM_SETICON,(WPARAM) ICON_BIG, (LPARAM) hIcon);
|
||||||
|
SetWindowText(hDlg, "Mongoose settings");
|
||||||
|
SetFocus(GetDlgItem(hDlg, ID_SAVE));
|
||||||
|
for (i = 0; options[i] != NULL; i += 3) {
|
||||||
|
name = options[i + 1];
|
||||||
|
value = mg_get_option(ctx, name);
|
||||||
|
if (is_boolean_option(name)) {
|
||||||
|
CheckDlgButton(hDlg, ID_CONTROLS + i / 3, !strcmp(value, "yes") ?
|
||||||
|
BST_CHECKED : BST_UNCHECKED);
|
||||||
|
} else {
|
||||||
|
SetDlgItemText(hDlg, ID_CONTROLS + i / 3, value == NULL ? "" : value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type,
|
||||||
|
DWORD id, DWORD style, WORD x, WORD y,
|
||||||
|
WORD cx, WORD cy, const char *caption) {
|
||||||
|
DLGITEMTEMPLATE *tp;
|
||||||
|
LPWORD p;
|
||||||
|
|
||||||
|
dia->cdit++;
|
||||||
|
|
||||||
|
*mem = align(*mem, 3);
|
||||||
|
tp = (DLGITEMTEMPLATE *) *mem;
|
||||||
|
|
||||||
|
tp->id = (WORD)id;
|
||||||
|
tp->style = style;
|
||||||
|
tp->dwExtendedStyle = 0;
|
||||||
|
tp->x = x;
|
||||||
|
tp->y = y;
|
||||||
|
tp->cx = cx;
|
||||||
|
tp->cy = cy;
|
||||||
|
|
||||||
|
p = align(*mem + sizeof(*tp), 1);
|
||||||
|
*p++ = 0xffff;
|
||||||
|
*p++ = type;
|
||||||
|
|
||||||
|
while (*caption != '\0') {
|
||||||
|
*p++ = (WCHAR) *caption++;
|
||||||
|
}
|
||||||
|
*p++ = 0;
|
||||||
|
p = align(p, 1);
|
||||||
|
|
||||||
|
*p++ = 0;
|
||||||
|
*mem = (unsigned char *) p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_settings_dialog() {
|
||||||
|
#define HEIGHT 15
|
||||||
|
#define WIDTH 400
|
||||||
|
#define LABEL_WIDTH 80
|
||||||
|
|
||||||
|
unsigned char mem[4096], *p;
|
||||||
|
const char **option_names, *long_option_name;
|
||||||
|
DWORD style;
|
||||||
|
DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
|
||||||
|
WORD i, cl, x, y, width, nelems = 0;
|
||||||
|
static int guard;
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
DLGTEMPLATE template; // 18 bytes
|
||||||
|
WORD menu, class;
|
||||||
|
wchar_t caption[1];
|
||||||
|
WORD fontsiz;
|
||||||
|
wchar_t fontface[7];
|
||||||
|
} dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
|
||||||
|
DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW, 0, 200, 200, WIDTH, 0},
|
||||||
|
0, 0, L"", 8, L"Tahoma"};
|
||||||
|
|
||||||
|
if (guard == 0) {
|
||||||
|
guard++;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) memset(mem, 0, sizeof(mem));
|
||||||
|
(void) memcpy(mem, &dialog_header, sizeof(dialog_header));
|
||||||
|
p = mem + sizeof(dialog_header);
|
||||||
|
|
||||||
|
option_names = mg_get_valid_option_names();
|
||||||
|
for (i = 0; option_names[i] != NULL; i += 3) {
|
||||||
|
long_option_name = option_names[i + 1];
|
||||||
|
style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
|
||||||
|
x = 10 + (WIDTH / 2) * (nelems % 2);
|
||||||
|
y = (nelems/2 + 1) * HEIGHT + 5;
|
||||||
|
width = WIDTH / 2 - 20 - LABEL_WIDTH;
|
||||||
|
if (is_numeric_options(long_option_name)) {
|
||||||
|
style |= ES_NUMBER;
|
||||||
|
cl = 0x81;
|
||||||
|
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||||
|
} else if (is_boolean_option(long_option_name)) {
|
||||||
|
cl = 0x80;
|
||||||
|
style |= BS_AUTOCHECKBOX;
|
||||||
|
} else if (is_filename_option(long_option_name) ||
|
||||||
|
is_directory_option(long_option_name)) {
|
||||||
|
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||||
|
width -= 20;
|
||||||
|
cl = 0x81;
|
||||||
|
add_control(&p, dia, 0x80,
|
||||||
|
ID_CONTROLS + (i / 3) + ID_FILE_BUTTONS_DELTA,
|
||||||
|
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
|
||||||
|
(WORD) (x + width + LABEL_WIDTH + 5),
|
||||||
|
y, 15, 12, "...");
|
||||||
|
} else {
|
||||||
|
cl = 0x81;
|
||||||
|
style |= WS_BORDER | ES_AUTOHSCROLL;
|
||||||
|
}
|
||||||
|
add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
|
||||||
|
x, y, LABEL_WIDTH, HEIGHT, long_option_name);
|
||||||
|
add_control(&p, dia, cl, ID_CONTROLS + (i / 3), style,
|
||||||
|
(WORD) (x + LABEL_WIDTH), y, width, 12, "");
|
||||||
|
nelems++;
|
||||||
|
}
|
||||||
|
|
||||||
|
y = (WORD) (((nelems + 1) / 2 + 1) * HEIGHT + 5);
|
||||||
|
add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
|
||||||
|
BS_GROUPBOX, 5, 5, WIDTH - 10, y, " Settings ");
|
||||||
|
y += 10;
|
||||||
|
add_control(&p, dia, 0x80, ID_SAVE,
|
||||||
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
||||||
|
WIDTH - 70, y, 65, 12, "Save Settings");
|
||||||
|
add_control(&p, dia, 0x80, ID_RESET_DEFAULTS,
|
||||||
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
|
||||||
|
WIDTH - 140, y, 65, 12, "Reset to defaults");
|
||||||
|
add_control(&p, dia, 0x82, ID_STATIC,
|
||||||
|
WS_CHILD | WS_VISIBLE | WS_DISABLED,
|
||||||
|
5, y, 180, 12, server_name);
|
||||||
|
|
||||||
|
dia->cy = ((nelems + 1) / 2 + 1) * HEIGHT + 30;
|
||||||
|
DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) NULL);
|
||||||
|
guard--;
|
||||||
|
}
|
||||||
|
|
||||||
static int manage_service(int action) {
|
static int manage_service(int action) {
|
||||||
static const char *service_name = "Mongoose";
|
static const char *service_name = "Mongoose";
|
||||||
SC_HANDLE hSCM = NULL, hService = NULL;
|
SC_HANDLE hSCM = NULL, hService = NULL;
|
||||||
@@ -409,14 +706,19 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
mg_stop(ctx);
|
mg_stop(ctx);
|
||||||
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
Shell_NotifyIcon(NIM_DELETE, &TrayIcon);
|
||||||
PostQuitMessage(0);
|
PostQuitMessage(0);
|
||||||
break;
|
return 0;
|
||||||
case ID_EDIT_CONFIG:
|
case ID_SETTINGS:
|
||||||
edit_config_file();
|
show_settings_dialog();
|
||||||
break;
|
break;
|
||||||
case ID_INSTALL_SERVICE:
|
case ID_INSTALL_SERVICE:
|
||||||
case ID_REMOVE_SERVICE:
|
case ID_REMOVE_SERVICE:
|
||||||
manage_service(LOWORD(wParam));
|
manage_service(LOWORD(wParam));
|
||||||
break;
|
break;
|
||||||
|
case ID_CONNECT:
|
||||||
|
printf("[%s]\n", get_url_to_first_open_port(ctx));
|
||||||
|
ShellExecute(NULL, "open", get_url_to_first_open_port(ctx),
|
||||||
|
NULL, NULL, SW_SHOW);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WM_USER:
|
case WM_USER:
|
||||||
@@ -436,7 +738,9 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
|
|||||||
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
|
AppendMenu(hMenu, MF_STRING | (!service_installed ? MF_GRAYED : 0),
|
||||||
ID_REMOVE_SERVICE, "Deinstall service");
|
ID_REMOVE_SERVICE, "Deinstall service");
|
||||||
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||||
AppendMenu(hMenu, MF_STRING, ID_EDIT_CONFIG, "Edit config file");
|
AppendMenu(hMenu, MF_STRING, ID_CONNECT, "Start browser");
|
||||||
|
AppendMenu(hMenu, MF_STRING, ID_SETTINGS, "Edit Settings");
|
||||||
|
AppendMenu(hMenu, MF_SEPARATOR, ID_SEPARATOR, "");
|
||||||
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
|
AppendMenu(hMenu, MF_STRING, ID_QUIT, "Exit");
|
||||||
GetCursorPos(&pt);
|
GetCursorPos(&pt);
|
||||||
SetForegroundWindow(hWnd);
|
SetForegroundWindow(hWnd);
|
||||||
@@ -473,10 +777,11 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
|||||||
ShowWindow(hWnd, SW_HIDE);
|
ShowWindow(hWnd, SW_HIDE);
|
||||||
|
|
||||||
TrayIcon.cbSize = sizeof(TrayIcon);
|
TrayIcon.cbSize = sizeof(TrayIcon);
|
||||||
TrayIcon.uID = ID_TRAYICON;
|
TrayIcon.uID = ID_ICON;
|
||||||
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
TrayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||||
TrayIcon.hIcon = LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON),
|
TrayIcon.hIcon = hIcon = LoadImage(GetModuleHandle(NULL),
|
||||||
IMAGE_ICON, 16, 16, 0);
|
MAKEINTRESOURCE(ID_ICON),
|
||||||
|
IMAGE_ICON, 16, 16, 0);
|
||||||
TrayIcon.hWnd = hWnd;
|
TrayIcon.hWnd = hWnd;
|
||||||
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
|
snprintf(TrayIcon.szTip, sizeof(TrayIcon.szTip), "%s", server_name);
|
||||||
TrayIcon.uCallbackMessage = WM_USER;
|
TrayIcon.uCallbackMessage = WM_USER;
|
||||||
@@ -499,14 +804,16 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR cmdline, int show) {
|
|||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation Mongoose
|
@implementation Mongoose
|
||||||
- (void) openBrowser {
|
- (void) openBrowser {
|
||||||
[[NSWorkspace sharedWorkspace]
|
[[NSWorkspace sharedWorkspace]
|
||||||
openURL:[NSURL URLWithString:
|
openURL:[NSURL URLWithString:
|
||||||
[NSString stringWithUTF8String:"http://www.yahoo.com"]]];
|
[NSString stringWithUTF8String:"http://www.yahoo.com"]]];
|
||||||
}
|
}
|
||||||
- (void) editConfig {
|
- (void) editConfig {
|
||||||
|
create_config_file(config_file);
|
||||||
[[NSWorkspace sharedWorkspace]
|
[[NSWorkspace sharedWorkspace]
|
||||||
openFile:@"mongoose.conf" withApplication:@"TextEdit"];
|
openFile:[NSString stringWithUTF8String:config_file]
|
||||||
|
withApplication:@"TextEdit"];
|
||||||
}
|
}
|
||||||
- (void)shutDown{
|
- (void)shutDown{
|
||||||
[NSApp terminate:nil];
|
[NSApp terminate:nil];
|
||||||
|
|||||||
+101
-85
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2004-2012 Sergey Lyubka
|
// Copyright (c) 2004-2013 Sergey Lyubka
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -30,8 +30,10 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined (_MSC_VER)
|
#if defined (_MSC_VER)
|
||||||
#pragma warning (disable : 4127) // conditional expression is constant: introduced by FD_SET(..)
|
// conditional expression is constant: introduced by FD_SET(..)
|
||||||
#pragma warning (disable : 4204) // non-constant aggregate initializer: issued due to missing C99 support
|
#pragma warning (disable : 4127)
|
||||||
|
// non-constant aggregate initializer: issued due to missing C99 support
|
||||||
|
#pragma warning (disable : 4204)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Disable WIN32_LEAN_AND_MEAN.
|
// Disable WIN32_LEAN_AND_MEAN.
|
||||||
@@ -149,9 +151,7 @@ typedef DWORD pthread_t;
|
|||||||
|
|
||||||
static int pthread_mutex_lock(pthread_mutex_t *);
|
static int pthread_mutex_lock(pthread_mutex_t *);
|
||||||
static int pthread_mutex_unlock(pthread_mutex_t *);
|
static int pthread_mutex_unlock(pthread_mutex_t *);
|
||||||
|
|
||||||
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
|
static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len);
|
||||||
|
|
||||||
struct file;
|
struct file;
|
||||||
static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p);
|
static char *mg_fgets(char *buf, size_t size, struct file *filep, char **p);
|
||||||
|
|
||||||
@@ -176,13 +176,23 @@ typedef struct DIR {
|
|||||||
struct dirent result;
|
struct dirent result;
|
||||||
} DIR;
|
} DIR;
|
||||||
|
|
||||||
|
#ifndef HAS_POLL
|
||||||
|
struct pollfd {
|
||||||
|
int fd;
|
||||||
|
short events;
|
||||||
|
short revents;
|
||||||
|
};
|
||||||
|
#define POLLIN 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Mark required libraries
|
// Mark required libraries
|
||||||
#pragma comment(lib, "Ws2_32.lib")
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
#else // UNIX specific
|
#else // UNIX specific
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/select.h>
|
#include <sys/poll.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -231,7 +241,7 @@ typedef int SOCKET;
|
|||||||
#include <lauxlib.h>
|
#include <lauxlib.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MONGOOSE_VERSION "3.5"
|
#define MONGOOSE_VERSION "3.6"
|
||||||
#define PASSWORDS_FILE_NAME ".htpasswd"
|
#define PASSWORDS_FILE_NAME ".htpasswd"
|
||||||
#define CGI_ENVIRONMENT_SIZE 4096
|
#define CGI_ENVIRONMENT_SIZE 4096
|
||||||
#define MAX_CGI_ENVIR_VARS 64
|
#define MAX_CGI_ENVIR_VARS 64
|
||||||
@@ -435,11 +445,11 @@ struct file {
|
|||||||
// Describes listening socket, or socket which was accept()-ed by the master
|
// Describes listening socket, or socket which was accept()-ed by the master
|
||||||
// thread and queued for future handling by the worker thread.
|
// thread and queued for future handling by the worker thread.
|
||||||
struct socket {
|
struct socket {
|
||||||
struct socket *next; // Linkage
|
|
||||||
SOCKET sock; // Listening socket
|
SOCKET sock; // Listening socket
|
||||||
union usa lsa; // Local socket address
|
union usa lsa; // Local socket address
|
||||||
union usa rsa; // Remote socket address
|
union usa rsa; // Remote socket address
|
||||||
int is_ssl; // Is socket SSL-ed
|
unsigned is_ssl:1; // Is port SSL-ed
|
||||||
|
unsigned ssl_redir:1; // Is port supposed to redirect everything to SSL port
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
|
// NOTE(lsm): this enum shoulds be in sync with the config_options below.
|
||||||
@@ -456,7 +466,7 @@ enum {
|
|||||||
static const char *config_options[] = {
|
static const char *config_options[] = {
|
||||||
"C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
|
"C", "cgi_pattern", "**.cgi$|**.pl$|**.php$",
|
||||||
"E", "cgi_environment", NULL,
|
"E", "cgi_environment", NULL,
|
||||||
"G", "put_delete_passwords_file", NULL,
|
"G", "put_delete_auth_file", NULL,
|
||||||
"I", "cgi_interpreter", NULL,
|
"I", "cgi_interpreter", NULL,
|
||||||
"P", "protect_uri", NULL,
|
"P", "protect_uri", NULL,
|
||||||
"R", "authentication_domain", "mydomain.com",
|
"R", "authentication_domain", "mydomain.com",
|
||||||
@@ -465,7 +475,7 @@ static const char *config_options[] = {
|
|||||||
"a", "access_log_file", NULL,
|
"a", "access_log_file", NULL,
|
||||||
"d", "enable_directory_listing", "yes",
|
"d", "enable_directory_listing", "yes",
|
||||||
"e", "error_log_file", NULL,
|
"e", "error_log_file", NULL,
|
||||||
"g", "global_passwords_file", NULL,
|
"g", "global_auth_file", NULL,
|
||||||
"i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
|
"i", "index_files", "index.html,index.htm,index.cgi,index.shtml,index.php",
|
||||||
"k", "enable_keep_alive", "no",
|
"k", "enable_keep_alive", "no",
|
||||||
"l", "access_control_list", NULL,
|
"l", "access_control_list", NULL,
|
||||||
@@ -490,6 +500,7 @@ struct mg_context {
|
|||||||
void *user_data; // User-defined data
|
void *user_data; // User-defined data
|
||||||
|
|
||||||
struct socket *listening_sockets;
|
struct socket *listening_sockets;
|
||||||
|
int num_listening_sockets;
|
||||||
|
|
||||||
volatile int num_threads; // Number of threads
|
volatile int num_threads; // Number of threads
|
||||||
pthread_mutex_t mutex; // Protects (max|num)_threads
|
pthread_mutex_t mutex; // Protects (max|num)_threads
|
||||||
@@ -1158,7 +1169,7 @@ static int mg_mkdir(const char *path, int mode) {
|
|||||||
mg_strlcpy(buf, path, sizeof(buf));
|
mg_strlcpy(buf, path, sizeof(buf));
|
||||||
change_slashes_to_backslashes(buf);
|
change_slashes_to_backslashes(buf);
|
||||||
|
|
||||||
(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
|
(void) MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, ARRAY_SIZE(wbuf));
|
||||||
|
|
||||||
return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
|
return CreateDirectoryW(wbuf, NULL) ? 0 : -1;
|
||||||
}
|
}
|
||||||
@@ -1231,6 +1242,33 @@ static struct dirent *readdir(DIR *dir) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_POLL
|
||||||
|
static int poll(struct pollfd *pfd, int n, int milliseconds) {
|
||||||
|
struct timeval tv;
|
||||||
|
fd_set set;
|
||||||
|
int i, result;
|
||||||
|
|
||||||
|
tv.tv_sec = milliseconds / 1000;
|
||||||
|
tv.tv_usec = (milliseconds % 1000) * 1000;
|
||||||
|
FD_ZERO(&set);
|
||||||
|
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
FD_SET((SOCKET) pfd[i].fd, &set);
|
||||||
|
pfd[i].revents = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result = select(0, &set, NULL, NULL, &tv)) > 0) {
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (FD_ISSET(pfd[i].fd, &set)) {
|
||||||
|
pfd[i].revents = POLLIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif // HAVE_POLL
|
||||||
|
|
||||||
#define set_close_on_exec(x) // No FD_CLOEXEC on Windows
|
#define set_close_on_exec(x) // No FD_CLOEXEC on Windows
|
||||||
|
|
||||||
int mg_start_thread(mg_thread_func_t f, void *p) {
|
int mg_start_thread(mg_thread_func_t f, void *p) {
|
||||||
@@ -1470,17 +1508,14 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
|
|||||||
// reading, must give up and close the connection and exit serving thread.
|
// reading, must give up and close the connection and exit serving thread.
|
||||||
static int wait_until_socket_is_readable(struct mg_connection *conn) {
|
static int wait_until_socket_is_readable(struct mg_connection *conn) {
|
||||||
int result;
|
int result;
|
||||||
struct timeval tv;
|
struct pollfd pfd;
|
||||||
fd_set set;
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
tv.tv_sec = 0;
|
pfd.fd = conn->client.sock;
|
||||||
tv.tv_usec = 300 * 1000;
|
pfd.events = POLLIN;
|
||||||
FD_ZERO(&set);
|
result = poll(&pfd, 1, 200);
|
||||||
FD_SET(conn->client.sock, &set);
|
if (result == 0 && conn->ssl != NULL) {
|
||||||
result = select(conn->client.sock + 1, &set, NULL, NULL, &tv);
|
result = SSL_pending(conn->ssl);
|
||||||
if(result == 0 && conn->ssl != NULL) {
|
|
||||||
result = SSL_pending(conn->ssl);
|
|
||||||
}
|
}
|
||||||
} while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
|
} while ((result == 0 || (result < 0 && ERRNO == EINTR)) &&
|
||||||
conn->ctx->stop_flag == 0);
|
conn->ctx->stop_flag == 0);
|
||||||
@@ -1609,7 +1644,8 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
|
|||||||
// vsnprintf() error, give up
|
// vsnprintf() error, give up
|
||||||
len = -1;
|
len = -1;
|
||||||
cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
|
cry(conn, "%s(%s, ...): vsnprintf() error", __func__, fmt);
|
||||||
} else if (len > (int) sizeof(mem) && (buf = (char *) malloc(len + 1)) != NULL) {
|
} else if (len > (int) sizeof(mem) &&
|
||||||
|
(buf = (char *) malloc(len + 1)) != NULL) {
|
||||||
// Local buffer is not large enough, allocate big buffer on heap
|
// Local buffer is not large enough, allocate big buffer on heap
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(buf, len + 1, fmt, ap);
|
vsnprintf(buf, len + 1, fmt, ap);
|
||||||
@@ -4141,9 +4177,9 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
|
|||||||
if ((s = strrchr(fname, '/')) == NULL) {
|
if ((s = strrchr(fname, '/')) == NULL) {
|
||||||
s = fname;
|
s = fname;
|
||||||
}
|
}
|
||||||
// Open file in binary mode with exclusive lock set
|
// Open file in binary mode. TODO: set an exclusive lock.
|
||||||
snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
|
snprintf(path, sizeof(path), "%s/%s", destination_dir, s);
|
||||||
if ((fp = fopen(path, "wbx")) == NULL) {
|
if ((fp = fopen(path, "wb")) == NULL) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4272,12 +4308,11 @@ static void handle_request(struct mg_connection *conn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void close_all_listening_sockets(struct mg_context *ctx) {
|
static void close_all_listening_sockets(struct mg_context *ctx) {
|
||||||
struct socket *sp, *tmp;
|
int i;
|
||||||
for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
|
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||||
tmp = sp->next;
|
closesocket(ctx->listening_sockets[i].sock);
|
||||||
(void) closesocket(sp->sock);
|
|
||||||
free(sp);
|
|
||||||
}
|
}
|
||||||
|
free(ctx->listening_sockets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid listening port specification is: [ip_address:]port[s]
|
// Valid listening port specification is: [ip_address:]port[s]
|
||||||
@@ -4297,11 +4332,13 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
|
|||||||
} else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
|
} else if (sscanf(vec->ptr, "%d%n", &port, &len) != 1 ||
|
||||||
len <= 0 ||
|
len <= 0 ||
|
||||||
len > (int) vec->len ||
|
len > (int) vec->len ||
|
||||||
(vec->ptr[len] && vec->ptr[len] != 's' && vec->ptr[len] != ',')) {
|
(vec->ptr[len] && vec->ptr[len] != 's' &&
|
||||||
|
vec->ptr[len] != 'r' && vec->ptr[len] != ',')) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
so->is_ssl = vec->ptr[len] == 's';
|
so->is_ssl = vec->ptr[len] == 's';
|
||||||
|
so->ssl_redir = vec->ptr[len] == 'r';
|
||||||
#if defined(USE_IPV6)
|
#if defined(USE_IPV6)
|
||||||
so->lsa.sin6.sin6_family = AF_INET6;
|
so->lsa.sin6.sin6_family = AF_INET6;
|
||||||
so->lsa.sin6.sin6_port = htons((uint16_t) port);
|
so->lsa.sin6.sin6_port = htons((uint16_t) port);
|
||||||
@@ -4316,9 +4353,8 @@ static int parse_port_string(const struct vec *vec, struct socket *so) {
|
|||||||
static int set_ports_option(struct mg_context *ctx) {
|
static int set_ports_option(struct mg_context *ctx) {
|
||||||
const char *list = ctx->config[LISTENING_PORTS];
|
const char *list = ctx->config[LISTENING_PORTS];
|
||||||
int on = 1, success = 1;
|
int on = 1, success = 1;
|
||||||
SOCKET sock;
|
|
||||||
struct vec vec;
|
struct vec vec;
|
||||||
struct socket so, *listener;
|
struct socket so;
|
||||||
|
|
||||||
while (success && (list = next_option(list, &vec, NULL)) != NULL) {
|
while (success && (list = next_option(list, &vec, NULL)) != NULL) {
|
||||||
if (!parse_port_string(&vec, &so)) {
|
if (!parse_port_string(&vec, &so)) {
|
||||||
@@ -4329,11 +4365,11 @@ static int set_ports_option(struct mg_context *ctx) {
|
|||||||
(ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
|
(ctx->ssl_ctx == NULL || ctx->config[SSL_CERTIFICATE] == NULL)) {
|
||||||
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
|
cry(fc(ctx), "Cannot add SSL socket, is -ssl_certificate option set?");
|
||||||
success = 0;
|
success = 0;
|
||||||
} else if ((sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
} else if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) ==
|
||||||
INVALID_SOCKET ||
|
INVALID_SOCKET ||
|
||||||
// On Windows, SO_REUSEADDR is recommended only for
|
// On Windows, SO_REUSEADDR is recommended only for
|
||||||
// broadcast UDP sockets
|
// broadcast UDP sockets
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on,
|
setsockopt(so.sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &on,
|
||||||
sizeof(on)) != 0 ||
|
sizeof(on)) != 0 ||
|
||||||
// Set TCP keep-alive. This is needed because if HTTP-level
|
// Set TCP keep-alive. This is needed because if HTTP-level
|
||||||
// keep-alive is enabled, and client resets the connection,
|
// keep-alive is enabled, and client resets the connection,
|
||||||
@@ -4342,27 +4378,22 @@ static int set_ports_option(struct mg_context *ctx) {
|
|||||||
// handshake will figure out that the client is down and
|
// handshake will figure out that the client is down and
|
||||||
// will close the server end.
|
// will close the server end.
|
||||||
// Thanks to Igor Klopov who suggested the patch.
|
// Thanks to Igor Klopov who suggested the patch.
|
||||||
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
|
setsockopt(so.sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
|
||||||
sizeof(on)) != 0 ||
|
sizeof(on)) != 0 ||
|
||||||
bind(sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
bind(so.sock, &so.lsa.sa, sizeof(so.lsa)) != 0 ||
|
||||||
listen(sock, SOMAXCONN) != 0) {
|
listen(so.sock, SOMAXCONN) != 0) {
|
||||||
closesocket(sock);
|
closesocket(so.sock);
|
||||||
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
|
cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
|
||||||
(int) vec.len, vec.ptr, strerror(ERRNO));
|
(int) vec.len, vec.ptr, strerror(ERRNO));
|
||||||
success = 0;
|
success = 0;
|
||||||
} else if ((listener = (struct socket *)
|
|
||||||
calloc(1, sizeof(*listener))) == NULL) {
|
|
||||||
// NOTE(lsm): order is important: call cry before closesocket(),
|
|
||||||
// cause closesocket() alters the errno.
|
|
||||||
cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
|
|
||||||
closesocket(sock);
|
|
||||||
success = 0;
|
|
||||||
} else {
|
} else {
|
||||||
*listener = so;
|
set_close_on_exec(so.sock);
|
||||||
listener->sock = sock;
|
// TODO: handle realloc failure
|
||||||
set_close_on_exec(listener->sock);
|
ctx->listening_sockets = realloc(ctx->listening_sockets,
|
||||||
listener->next = ctx->listening_sockets;
|
(ctx->num_listening_sockets + 1) *
|
||||||
ctx->listening_sockets = listener;
|
sizeof(ctx->listening_sockets[0]));
|
||||||
|
ctx->listening_sockets[ctx->num_listening_sockets] = so;
|
||||||
|
ctx->num_listening_sockets++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4443,13 +4474,6 @@ static int check_acl(struct mg_context *ctx, uint32_t remote_ip) {
|
|||||||
return allowed == '+';
|
return allowed == '+';
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
|
|
||||||
FD_SET(fd, set);
|
|
||||||
if (fd > (SOCKET) *max_fd) {
|
|
||||||
*max_fd = (int) fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
static int set_uid_option(struct mg_context *ctx) {
|
static int set_uid_option(struct mg_context *ctx) {
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
@@ -4826,6 +4850,7 @@ static void process_new_connection(struct mg_connection *conn) {
|
|||||||
}
|
}
|
||||||
conn->birth_time = time(NULL);
|
conn->birth_time = time(NULL);
|
||||||
handle_request(conn);
|
handle_request(conn);
|
||||||
|
conn->request_info.ev_data = (void *) conn->status_code;
|
||||||
call_user(conn, MG_REQUEST_COMPLETE);
|
call_user(conn, MG_REQUEST_COMPLETE);
|
||||||
log_access(conn);
|
log_access(conn);
|
||||||
}
|
}
|
||||||
@@ -4884,7 +4909,8 @@ static int consume_socket(struct mg_context *ctx, struct socket *sp) {
|
|||||||
return !ctx->stop_flag;
|
return !ctx->stop_flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void worker_thread(struct mg_context *ctx) {
|
static void *worker_thread(void *thread_func_param) {
|
||||||
|
struct mg_context *ctx = thread_func_param;
|
||||||
struct mg_connection *conn;
|
struct mg_connection *conn;
|
||||||
|
|
||||||
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
|
conn = (struct mg_connection *) calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE);
|
||||||
@@ -4929,6 +4955,7 @@ static void worker_thread(struct mg_context *ctx) {
|
|||||||
(void) pthread_mutex_unlock(&ctx->mutex);
|
(void) pthread_mutex_unlock(&ctx->mutex);
|
||||||
|
|
||||||
DEBUG_TRACE(("exiting"));
|
DEBUG_TRACE(("exiting"));
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Master thread adds accepted socket to a queue
|
// Master thread adds accepted socket to a queue
|
||||||
@@ -4977,11 +5004,10 @@ static void accept_new_connection(const struct socket *listener,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void master_thread(struct mg_context *ctx) {
|
static void *master_thread(void *thread_func_param) {
|
||||||
fd_set read_set;
|
struct mg_context *ctx = thread_func_param;
|
||||||
struct timeval tv;
|
struct pollfd *pfd;
|
||||||
struct socket *sp;
|
int i;
|
||||||
int max_fd;
|
|
||||||
|
|
||||||
// Increase priority of the master thread
|
// Increase priority of the master thread
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@@ -4994,33 +5020,22 @@ static void master_thread(struct mg_context *ctx) {
|
|||||||
pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
|
pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
pfd = calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
|
||||||
while (ctx->stop_flag == 0) {
|
while (ctx->stop_flag == 0) {
|
||||||
FD_ZERO(&read_set);
|
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||||
max_fd = -1;
|
pfd[i].fd = ctx->listening_sockets[i].sock;
|
||||||
|
pfd[i].events = POLLIN;
|
||||||
// Add listening sockets to the read set
|
|
||||||
for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
|
|
||||||
add_to_set(sp->sock, &read_set, &max_fd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tv.tv_sec = 0;
|
if (poll(pfd, ctx->num_listening_sockets, 200) > 0) {
|
||||||
tv.tv_usec = 200 * 1000;
|
for (i = 0; i < ctx->num_listening_sockets; i++) {
|
||||||
|
if (ctx->stop_flag == 0 && pfd[i].revents == POLLIN) {
|
||||||
if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
|
accept_new_connection(&ctx->listening_sockets[i], ctx);
|
||||||
#ifdef _WIN32
|
|
||||||
// On windows, if read_set and write_set are empty,
|
|
||||||
// select() returns "Invalid parameter" error
|
|
||||||
// (at least on my Windows XP Pro). So in this case, we sleep here.
|
|
||||||
mg_sleep(1000);
|
|
||||||
#endif // _WIN32
|
|
||||||
} else {
|
|
||||||
for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
|
|
||||||
if (ctx->stop_flag == 0 && FD_ISSET(sp->sock, &read_set)) {
|
|
||||||
accept_new_connection(sp, ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
free(pfd);
|
||||||
DEBUG_TRACE(("stopping workers"));
|
DEBUG_TRACE(("stopping workers"));
|
||||||
|
|
||||||
// Stop signal received: somebody called mg_stop. Quit.
|
// Stop signal received: somebody called mg_stop. Quit.
|
||||||
@@ -5051,6 +5066,7 @@ static void master_thread(struct mg_context *ctx) {
|
|||||||
// WARNING: This must be the very last thing this
|
// WARNING: This must be the very last thing this
|
||||||
// thread does, as ctx becomes invalid after this line.
|
// thread does, as ctx becomes invalid after this line.
|
||||||
ctx->stop_flag = 2;
|
ctx->stop_flag = 2;
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_context(struct mg_context *ctx) {
|
static void free_context(struct mg_context *ctx) {
|
||||||
@@ -5172,11 +5188,11 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
|
|||||||
(void) pthread_cond_init(&ctx->sq_full, NULL);
|
(void) pthread_cond_init(&ctx->sq_full, NULL);
|
||||||
|
|
||||||
// Start master (listening) thread
|
// Start master (listening) thread
|
||||||
mg_start_thread((mg_thread_func_t) master_thread, ctx);
|
mg_start_thread(master_thread, ctx);
|
||||||
|
|
||||||
// Start worker threads
|
// Start worker threads
|
||||||
for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
|
for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
|
||||||
if (mg_start_thread((mg_thread_func_t) worker_thread, ctx) != 0) {
|
if (mg_start_thread(worker_thread, ctx) != 0) {
|
||||||
cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
|
cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
|
||||||
} else {
|
} else {
|
||||||
ctx->num_threads++;
|
ctx->num_threads++;
|
||||||
|
|||||||
+3
-2
@@ -47,7 +47,7 @@ struct mg_request_info {
|
|||||||
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 the mg_start()
|
void *user_data; // User data pointer passed to mg_start()
|
||||||
void *ev_data; // Event-specific data pointer
|
void *ev_data; // Event-specific data pointer
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -61,7 +61,8 @@ enum mg_event {
|
|||||||
|
|
||||||
// Mongoose has finished handling the request.
|
// Mongoose has finished handling the request.
|
||||||
// Callback return value is ignored.
|
// Callback return value is ignored.
|
||||||
// ev_data contains NULL.
|
// ev_data contains integer HTTP status code:
|
||||||
|
// int http_reply_status_code = (long) request_info->ev_data;
|
||||||
MG_REQUEST_COMPLETE,
|
MG_REQUEST_COMPLETE,
|
||||||
|
|
||||||
// HTTP error must be returned to the client.
|
// HTTP error must be returned to the client.
|
||||||
|
|||||||
+1
-1
@@ -171,7 +171,7 @@ my $cmd = "$exe ".
|
|||||||
"-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_passwords_file test/passfile ' .
|
'-put_delete_auth_file test/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 ".
|
||||||
|
|||||||
Reference in new issue
Block a user