Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongoose
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
esp
mongoose
Commits
37751a28
Commit
37751a28
authored
Nov 23, 2013
by
Sergey Lyubka
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
directory ops moved to directory.c
parent
45347bd4
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
520 additions
and
673 deletions
+520
-673
Makefile
build/Makefile
+1
-1
directory.c
build/src/directory.c
+238
-0
internal.h
build/src/internal.h
+1
-4
mongoose.c
build/src/mongoose.c
+2
-325
string.c
build/src/string.c
+19
-0
mongoose.c
mongoose.c
+259
-329
unit_test.c
test/unit_test.c
+0
-14
No files found.
build/Makefile
View file @
37751a28
...
@@ -29,7 +29,7 @@ VERSION = $(shell perl -lne \
...
@@ -29,7 +29,7 @@ VERSION = $(shell perl -lne \
SOURCES
=
src/internal.h src/util.c src/string.c src/parse_date.c
\
SOURCES
=
src/internal.h src/util.c src/string.c src/parse_date.c
\
src/options.c src/crypto.c src/auth.c src/win32.c src/unix.c
\
src/options.c src/crypto.c src/auth.c src/win32.c src/unix.c
\
src/mg_printf.c src/ssl.c src/http_client.c src/mime.c
\
src/mg_printf.c src/ssl.c src/http_client.c src/mime.c
\
src/mongoose.c src/lua.c
src/
directory.c src/
mongoose.c src/lua.c
TINY_SOURCES
=
../mongoose.c main.c
TINY_SOURCES
=
../mongoose.c main.c
LUA_SOURCES
=
$(TINY_SOURCES)
sqlite3.c lsqlite3.c lua_5.2.1.c
LUA_SOURCES
=
$(TINY_SOURCES)
sqlite3.c lsqlite3.c lua_5.2.1.c
...
...
build/src/directory.c
0 → 100644
View file @
37751a28
#include "internal.h"
static
void
print_dir_entry
(
const
struct
de
*
de
)
{
char
size
[
64
],
mod
[
64
],
href
[
PATH_MAX
*
3
];
const
char
*
slash
=
de
->
file
.
is_directory
?
"/"
:
""
;
if
(
de
->
file
.
is_directory
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%s"
,
"[DIRECTORY]"
);
}
else
{
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double. Sigh.
if
(
de
->
file
.
size
<
1024
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%d"
,
(
int
)
de
->
file
.
size
);
}
else
if
(
de
->
file
.
size
<
0x100000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fk"
,
(
double
)
de
->
file
.
size
/
1024
.
0
);
}
else
if
(
de
->
file
.
size
<
0x40000000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fM"
,
(
double
)
de
->
file
.
size
/
1048576
);
}
else
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fG"
,
(
double
)
de
->
file
.
size
/
1073741824
);
}
}
strftime
(
mod
,
sizeof
(
mod
),
"%d-%b-%Y %H:%M"
,
localtime
(
&
de
->
file
.
modification_time
));
mg_url_encode
(
de
->
file_name
,
href
,
sizeof
(
href
));
de
->
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
de
->
conn
,
"<tr><td><a href=
\"
%s%s%s
\"
>%s%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
de
->
conn
->
request_info
.
uri
,
href
,
slash
,
de
->
file_name
,
slash
,
mod
,
size
);
}
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static
int
WINCDECL
compare_dir_entries
(
const
void
*
p1
,
const
void
*
p2
)
{
const
struct
de
*
a
=
(
const
struct
de
*
)
p1
,
*
b
=
(
const
struct
de
*
)
p2
;
const
char
*
query_string
=
a
->
conn
->
request_info
.
query_string
;
int
cmp_result
=
0
;
if
(
query_string
==
NULL
)
{
query_string
=
"na"
;
}
if
(
a
->
file
.
is_directory
&&
!
b
->
file
.
is_directory
)
{
return
-
1
;
// Always put directories on top
}
else
if
(
!
a
->
file
.
is_directory
&&
b
->
file
.
is_directory
)
{
return
1
;
// Always put directories on top
}
else
if
(
*
query_string
==
'n'
)
{
cmp_result
=
strcmp
(
a
->
file_name
,
b
->
file_name
);
}
else
if
(
*
query_string
==
's'
)
{
cmp_result
=
a
->
file
.
size
==
b
->
file
.
size
?
0
:
a
->
file
.
size
>
b
->
file
.
size
?
1
:
-
1
;
}
else
if
(
*
query_string
==
'd'
)
{
cmp_result
=
a
->
file
.
modification_time
==
b
->
file
.
modification_time
?
0
:
a
->
file
.
modification_time
>
b
->
file
.
modification_time
?
1
:
-
1
;
}
return
query_string
[
1
]
==
'd'
?
-
cmp_result
:
cmp_result
;
}
static
int
must_hide_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
pw_pattern
=
"**"
PASSWORDS_FILE_NAME
"$"
;
const
char
*
pattern
=
conn
->
ctx
->
config
[
HIDE_FILES
];
return
match_prefix
(
pw_pattern
,
strlen
(
pw_pattern
),
path
)
>
0
||
(
pattern
!=
NULL
&&
match_prefix
(
pattern
,
strlen
(
pattern
),
path
)
>
0
);
}
static
int
scan_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
,
void
*
data
,
void
(
*
cb
)(
struct
de
*
,
void
*
))
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir and hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
)
||
must_hide_file
(
conn
,
dp
->
d_name
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
de
.
file_name
=
dp
->
d_name
;
cb
(
&
de
,
data
);
}
(
void
)
closedir
(
dirp
);
}
return
1
;
}
static
int
remove_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir, but show hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
if
(
de
.
file
.
modification_time
)
{
if
(
de
.
file
.
is_directory
)
{
remove_directory
(
conn
,
path
);
}
else
{
mg_remove
(
path
);
}
}
}
(
void
)
closedir
(
dirp
);
rmdir
(
dir
);
}
return
1
;
}
struct
dir_scan_data
{
struct
de
*
entries
;
int
num_entries
;
int
arr_size
;
};
// Behaves like realloc(), but frees original pointer on failure
static
void
*
realloc2
(
void
*
ptr
,
size_t
size
)
{
void
*
new_ptr
=
realloc
(
ptr
,
size
);
if
(
new_ptr
==
NULL
)
{
free
(
ptr
);
}
return
new_ptr
;
}
static
void
dir_scan_callback
(
struct
de
*
de
,
void
*
data
)
{
struct
dir_scan_data
*
dsd
=
(
struct
dir_scan_data
*
)
data
;
if
(
dsd
->
entries
==
NULL
||
dsd
->
num_entries
>=
dsd
->
arr_size
)
{
dsd
->
arr_size
*=
2
;
dsd
->
entries
=
(
struct
de
*
)
realloc2
(
dsd
->
entries
,
dsd
->
arr_size
*
sizeof
(
dsd
->
entries
[
0
]));
}
if
(
dsd
->
entries
==
NULL
)
{
// TODO(lsm): propagate an error to the caller
dsd
->
num_entries
=
0
;
}
else
{
dsd
->
entries
[
dsd
->
num_entries
].
file_name
=
mg_strdup
(
de
->
file_name
);
dsd
->
entries
[
dsd
->
num_entries
].
file
=
de
->
file
;
dsd
->
entries
[
dsd
->
num_entries
].
conn
=
de
->
conn
;
dsd
->
num_entries
++
;
}
}
static
void
handle_directory_request
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
int
i
,
sort_direction
;
struct
dir_scan_data
data
=
{
NULL
,
0
,
128
};
if
(
!
scan_directory
(
conn
,
dir
,
&
data
,
dir_scan_callback
))
{
send_http_error
(
conn
,
500
,
"Cannot open directory"
,
"Error: opendir(%s): %s"
,
dir
,
strerror
(
ERRNO
));
return
;
}
sort_direction
=
conn
->
request_info
.
query_string
!=
NULL
&&
conn
->
request_info
.
query_string
[
1
]
==
'd'
?
'a'
:
'd'
;
conn
->
must_close
=
1
;
mg_printf
(
conn
,
"%s"
,
"HTTP/1.1 200 OK
\r\n
"
"Transfer-Encoding: Chunked
\r\n
"
"Content-Type: text/html; charset=utf-8
\r\n\r\n
"
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=
\"
0
\"
>"
"<tr><th><a href=
\"
?n%c
\"
>Name</a></th>"
"<th><a href=
\"
?d%c
\"
>Modified</a></th>"
"<th><a href=
\"
?s%c
\"
>Size</a></th></tr>"
"<tr><td colspan=
\"
3
\"
><hr></td></tr>"
,
conn
->
request_info
.
uri
,
conn
->
request_info
.
uri
,
sort_direction
,
sort_direction
,
sort_direction
);
// Print first entry - link to a parent directory
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<tr><td><a href=
\"
%s%s
\"
>%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
conn
->
request_info
.
uri
,
".."
,
"Parent directory"
,
"-"
,
"-"
);
// Sort and print directory entries
qsort
(
data
.
entries
,
(
size_t
)
data
.
num_entries
,
sizeof
(
data
.
entries
[
0
]),
compare_dir_entries
);
for
(
i
=
0
;
i
<
data
.
num_entries
;
i
++
)
{
print_dir_entry
(
&
data
.
entries
[
i
]);
free
(
data
.
entries
[
i
].
file_name
);
}
free
(
data
.
entries
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
num_bytes_sent
+=
mg_write
(
conn
,
"0
\r\n\r\n
"
,
5
);
conn
->
status_code
=
200
;
}
build/src/internal.h
View file @
37751a28
...
@@ -399,7 +399,7 @@ struct socket {
...
@@ -399,7 +399,7 @@ struct socket {
// NOTE(lsm): this enum shoulds be in sync with the config_options.
// NOTE(lsm): this enum shoulds be in sync with the config_options.
enum
{
enum
{
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
THROTTLE
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
ACCESS_LOG_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
ACCESS_LOG_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
...
@@ -446,9 +446,6 @@ struct mg_connection {
...
@@ -446,9 +446,6 @@ struct mg_connection {
int
request_len
;
// Size of the request + headers in a buffer
int
request_len
;
// Size of the request + headers in a buffer
int
data_len
;
// Total size of data in a buffer
int
data_len
;
// Total size of data in a buffer
int
status_code
;
// HTTP reply status code, e.g. 200
int
status_code
;
// HTTP reply status code, e.g. 200
int
throttle
;
// Throttling, bytes/sec. <= 0 means no throttle
time_t
last_throttle_time
;
// Last time throttled data was sent
int64_t
last_throttle_bytes
;
// Bytes sent this second
};
};
// Directory entry
// Directory entry
...
...
build/src/mongoose.c
View file @
37751a28
...
@@ -255,41 +255,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
...
@@ -255,41 +255,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
}
}
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
time_t
now
;
return
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
int64_t
n
,
total
,
allowed
;
if
(
conn
->
throttle
>
0
)
{
if
((
now
=
time
(
NULL
))
!=
conn
->
last_throttle_time
)
{
conn
->
last_throttle_time
=
now
;
conn
->
last_throttle_bytes
=
0
;
}
allowed
=
conn
->
throttle
-
conn
->
last_throttle_bytes
;
if
(
allowed
>
(
int64_t
)
len
)
{
allowed
=
len
;
}
if
((
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
==
allowed
)
{
buf
=
(
char
*
)
buf
+
total
;
conn
->
last_throttle_bytes
+=
total
;
while
(
total
<
(
int64_t
)
len
&&
conn
->
ctx
->
stop_flag
==
0
)
{
allowed
=
conn
->
throttle
>
(
int64_t
)
len
-
total
?
(
int64_t
)
len
-
total
:
conn
->
throttle
;
if
((
n
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
!=
allowed
)
{
break
;
}
sleep
(
1
);
conn
->
last_throttle_bytes
=
allowed
;
conn
->
last_throttle_time
=
time
(
NULL
);
buf
=
(
char
*
)
buf
+
n
;
total
+=
n
;
}
}
}
else
{
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
len
);
(
int64_t
)
len
);
}
return
(
int
)
total
;
}
}
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
...
@@ -503,262 +470,6 @@ static int get_request_len(const char *buf, int buf_len) {
...
@@ -503,262 +470,6 @@ static int get_request_len(const char *buf, int buf_len) {
return
0
;
return
0
;
}
}
void
mg_url_encode
(
const
char
*
src
,
char
*
dst
,
size_t
dst_len
)
{
static
const
char
*
dont_escape
=
"._-$,;~()"
;
static
const
char
*
hex
=
"0123456789abcdef"
;
const
char
*
end
=
dst
+
dst_len
-
1
;
for
(;
*
src
!=
'\0'
&&
dst
<
end
;
src
++
,
dst
++
)
{
if
(
isalnum
(
*
(
const
unsigned
char
*
)
src
)
||
strchr
(
dont_escape
,
*
(
const
unsigned
char
*
)
src
)
!=
NULL
)
{
*
dst
=
*
src
;
}
else
if
(
dst
+
2
<
end
)
{
dst
[
0
]
=
'%'
;
dst
[
1
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
>>
4
];
dst
[
2
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
&
0xf
];
dst
+=
2
;
}
}
*
dst
=
'\0'
;
}
static
void
print_dir_entry
(
const
struct
de
*
de
)
{
char
size
[
64
],
mod
[
64
],
href
[
PATH_MAX
*
3
];
const
char
*
slash
=
de
->
file
.
is_directory
?
"/"
:
""
;
if
(
de
->
file
.
is_directory
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%s"
,
"[DIRECTORY]"
);
}
else
{
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double. Sigh.
if
(
de
->
file
.
size
<
1024
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%d"
,
(
int
)
de
->
file
.
size
);
}
else
if
(
de
->
file
.
size
<
0x100000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fk"
,
(
double
)
de
->
file
.
size
/
1024
.
0
);
}
else
if
(
de
->
file
.
size
<
0x40000000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fM"
,
(
double
)
de
->
file
.
size
/
1048576
);
}
else
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fG"
,
(
double
)
de
->
file
.
size
/
1073741824
);
}
}
strftime
(
mod
,
sizeof
(
mod
),
"%d-%b-%Y %H:%M"
,
localtime
(
&
de
->
file
.
modification_time
));
mg_url_encode
(
de
->
file_name
,
href
,
sizeof
(
href
));
de
->
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
de
->
conn
,
"<tr><td><a href=
\"
%s%s%s
\"
>%s%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
de
->
conn
->
request_info
.
uri
,
href
,
slash
,
de
->
file_name
,
slash
,
mod
,
size
);
}
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static
int
WINCDECL
compare_dir_entries
(
const
void
*
p1
,
const
void
*
p2
)
{
const
struct
de
*
a
=
(
const
struct
de
*
)
p1
,
*
b
=
(
const
struct
de
*
)
p2
;
const
char
*
query_string
=
a
->
conn
->
request_info
.
query_string
;
int
cmp_result
=
0
;
if
(
query_string
==
NULL
)
{
query_string
=
"na"
;
}
if
(
a
->
file
.
is_directory
&&
!
b
->
file
.
is_directory
)
{
return
-
1
;
// Always put directories on top
}
else
if
(
!
a
->
file
.
is_directory
&&
b
->
file
.
is_directory
)
{
return
1
;
// Always put directories on top
}
else
if
(
*
query_string
==
'n'
)
{
cmp_result
=
strcmp
(
a
->
file_name
,
b
->
file_name
);
}
else
if
(
*
query_string
==
's'
)
{
cmp_result
=
a
->
file
.
size
==
b
->
file
.
size
?
0
:
a
->
file
.
size
>
b
->
file
.
size
?
1
:
-
1
;
}
else
if
(
*
query_string
==
'd'
)
{
cmp_result
=
a
->
file
.
modification_time
==
b
->
file
.
modification_time
?
0
:
a
->
file
.
modification_time
>
b
->
file
.
modification_time
?
1
:
-
1
;
}
return
query_string
[
1
]
==
'd'
?
-
cmp_result
:
cmp_result
;
}
static
int
must_hide_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
pw_pattern
=
"**"
PASSWORDS_FILE_NAME
"$"
;
const
char
*
pattern
=
conn
->
ctx
->
config
[
HIDE_FILES
];
return
match_prefix
(
pw_pattern
,
strlen
(
pw_pattern
),
path
)
>
0
||
(
pattern
!=
NULL
&&
match_prefix
(
pattern
,
strlen
(
pattern
),
path
)
>
0
);
}
static
int
scan_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
,
void
*
data
,
void
(
*
cb
)(
struct
de
*
,
void
*
))
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir and hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
)
||
must_hide_file
(
conn
,
dp
->
d_name
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
de
.
file_name
=
dp
->
d_name
;
cb
(
&
de
,
data
);
}
(
void
)
closedir
(
dirp
);
}
return
1
;
}
static
int
remove_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir, but show hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
if
(
de
.
file
.
modification_time
)
{
if
(
de
.
file
.
is_directory
)
{
remove_directory
(
conn
,
path
);
}
else
{
mg_remove
(
path
);
}
}
}
(
void
)
closedir
(
dirp
);
rmdir
(
dir
);
}
return
1
;
}
struct
dir_scan_data
{
struct
de
*
entries
;
int
num_entries
;
int
arr_size
;
};
// Behaves like realloc(), but frees original pointer on failure
static
void
*
realloc2
(
void
*
ptr
,
size_t
size
)
{
void
*
new_ptr
=
realloc
(
ptr
,
size
);
if
(
new_ptr
==
NULL
)
{
free
(
ptr
);
}
return
new_ptr
;
}
static
void
dir_scan_callback
(
struct
de
*
de
,
void
*
data
)
{
struct
dir_scan_data
*
dsd
=
(
struct
dir_scan_data
*
)
data
;
if
(
dsd
->
entries
==
NULL
||
dsd
->
num_entries
>=
dsd
->
arr_size
)
{
dsd
->
arr_size
*=
2
;
dsd
->
entries
=
(
struct
de
*
)
realloc2
(
dsd
->
entries
,
dsd
->
arr_size
*
sizeof
(
dsd
->
entries
[
0
]));
}
if
(
dsd
->
entries
==
NULL
)
{
// TODO(lsm): propagate an error to the caller
dsd
->
num_entries
=
0
;
}
else
{
dsd
->
entries
[
dsd
->
num_entries
].
file_name
=
mg_strdup
(
de
->
file_name
);
dsd
->
entries
[
dsd
->
num_entries
].
file
=
de
->
file
;
dsd
->
entries
[
dsd
->
num_entries
].
conn
=
de
->
conn
;
dsd
->
num_entries
++
;
}
}
static
void
handle_directory_request
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
int
i
,
sort_direction
;
struct
dir_scan_data
data
=
{
NULL
,
0
,
128
};
if
(
!
scan_directory
(
conn
,
dir
,
&
data
,
dir_scan_callback
))
{
send_http_error
(
conn
,
500
,
"Cannot open directory"
,
"Error: opendir(%s): %s"
,
dir
,
strerror
(
ERRNO
));
return
;
}
sort_direction
=
conn
->
request_info
.
query_string
!=
NULL
&&
conn
->
request_info
.
query_string
[
1
]
==
'd'
?
'a'
:
'd'
;
conn
->
must_close
=
1
;
mg_printf
(
conn
,
"%s"
,
"HTTP/1.1 200 OK
\r\n
"
"Transfer-Encoding: Chunked
\r\n
"
"Content-Type: text/html; charset=utf-8
\r\n\r\n
"
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=
\"
0
\"
>"
"<tr><th><a href=
\"
?n%c
\"
>Name</a></th>"
"<th><a href=
\"
?d%c
\"
>Modified</a></th>"
"<th><a href=
\"
?s%c
\"
>Size</a></th></tr>"
"<tr><td colspan=
\"
3
\"
><hr></td></tr>"
,
conn
->
request_info
.
uri
,
conn
->
request_info
.
uri
,
sort_direction
,
sort_direction
,
sort_direction
);
// Print first entry - link to a parent directory
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<tr><td><a href=
\"
%s%s
\"
>%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
conn
->
request_info
.
uri
,
".."
,
"Parent directory"
,
"-"
,
"-"
);
// Sort and print directory entries
qsort
(
data
.
entries
,
(
size_t
)
data
.
num_entries
,
sizeof
(
data
.
entries
[
0
]),
compare_dir_entries
);
for
(
i
=
0
;
i
<
data
.
num_entries
;
i
++
)
{
print_dir_entry
(
&
data
.
entries
[
i
]);
free
(
data
.
entries
[
i
].
file_name
);
}
free
(
data
.
entries
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
num_bytes_sent
+=
mg_write
(
conn
,
"0
\r\n\r\n
"
,
5
);
conn
->
status_code
=
200
;
}
// Send len bytes from the opened file to the client.
// Send len bytes from the opened file to the client.
static
void
send_file_data
(
struct
mg_connection
*
conn
,
FILE
*
fp
,
static
void
send_file_data
(
struct
mg_connection
*
conn
,
FILE
*
fp
,
int64_t
offset
,
int64_t
len
)
{
int64_t
offset
,
int64_t
len
)
{
...
@@ -2076,38 +1787,6 @@ static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
...
@@ -2076,38 +1787,6 @@ static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
return
len
;
return
len
;
}
}
static
int
set_throttle
(
const
char
*
spec
,
uint32_t
remote_ip
,
const
char
*
uri
)
{
int
throttle
=
0
;
struct
vec
vec
,
val
;
uint32_t
net
,
mask
;
char
mult
;
double
v
;
while
((
spec
=
next_option
(
spec
,
&
vec
,
&
val
))
!=
NULL
)
{
mult
=
','
;
if
(
sscanf
(
val
.
ptr
,
"%lf%c"
,
&
v
,
&
mult
)
<
1
||
v
<
0
||
(
lowercase
(
&
mult
)
!=
'k'
&&
lowercase
(
&
mult
)
!=
'm'
&&
mult
!=
','
))
{
continue
;
}
v
*=
lowercase
(
&
mult
)
==
'k'
?
1024
:
lowercase
(
&
mult
)
==
'm'
?
1048576
:
1
;
if
(
vec
.
len
==
1
&&
vec
.
ptr
[
0
]
==
'*'
)
{
throttle
=
(
int
)
v
;
}
else
if
(
parse_net
(
vec
.
ptr
,
&
net
,
&
mask
)
>
0
)
{
if
((
remote_ip
&
mask
)
==
net
)
{
throttle
=
(
int
)
v
;
}
}
else
if
(
match_prefix
(
vec
.
ptr
,
vec
.
len
,
uri
)
>
0
)
{
throttle
=
(
int
)
v
;
}
}
return
throttle
;
}
static
uint32_t
get_remote_ip
(
const
struct
mg_connection
*
conn
)
{
return
ntohl
(
*
(
uint32_t
*
)
&
conn
->
client
.
rsa
.
sin
.
sin_addr
);
}
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
char
*
path
,
int
path_len
)
{
char
*
path
,
int
path_len
)
{
const
char
*
content_type_header
,
*
boundary_start
;
const
char
*
content_type_header
,
*
boundary_start
;
...
@@ -2312,8 +1991,6 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -2312,8 +1991,6 @@ static void handle_request(struct mg_connection *conn) {
uri_len
=
(
int
)
strlen
(
ri
->
uri
);
uri_len
=
(
int
)
strlen
(
ri
->
uri
);
mg_url_decode
(
ri
->
uri
,
uri_len
,
(
char
*
)
ri
->
uri
,
uri_len
+
1
,
0
);
mg_url_decode
(
ri
->
uri
,
uri_len
,
(
char
*
)
ri
->
uri
,
uri_len
+
1
,
0
);
remove_double_dots_and_double_slashes
((
char
*
)
ri
->
uri
);
remove_double_dots_and_double_slashes
((
char
*
)
ri
->
uri
);
conn
->
throttle
=
set_throttle
(
conn
->
ctx
->
config
[
THROTTLE
],
get_remote_ip
(
conn
),
ri
->
uri
);
path
[
0
]
=
'\0'
;
path
[
0
]
=
'\0'
;
convert_uri_to_file_name
(
conn
,
path
,
sizeof
(
path
),
&
file
);
convert_uri_to_file_name
(
conn
,
path
,
sizeof
(
path
),
&
file
);
...
@@ -2607,7 +2284,7 @@ static void reset_per_request_attributes(struct mg_connection *conn) {
...
@@ -2607,7 +2284,7 @@ static void reset_per_request_attributes(struct mg_connection *conn) {
conn
->
path_info
=
NULL
;
conn
->
path_info
=
NULL
;
conn
->
num_bytes_sent
=
conn
->
num_bytes_read
=
0
;
conn
->
num_bytes_sent
=
conn
->
num_bytes_read
=
0
;
conn
->
status_code
=
-
1
;
conn
->
status_code
=
-
1
;
conn
->
must_close
=
conn
->
request_len
=
conn
->
throttle
=
0
;
conn
->
must_close
=
conn
->
request_len
=
0
;
}
}
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
...
...
build/src/string.c
View file @
37751a28
...
@@ -266,3 +266,22 @@ static void remove_double_dots_and_double_slashes(char *s) {
...
@@ -266,3 +266,22 @@ static void remove_double_dots_and_double_slashes(char *s) {
*
p
=
'\0'
;
*
p
=
'\0'
;
}
}
void
mg_url_encode
(
const
char
*
src
,
char
*
dst
,
size_t
dst_len
)
{
static
const
char
*
dont_escape
=
"._-$,;~()"
;
static
const
char
*
hex
=
"0123456789abcdef"
;
const
char
*
end
=
dst
+
dst_len
-
1
;
for
(;
*
src
!=
'\0'
&&
dst
<
end
;
src
++
,
dst
++
)
{
if
(
isalnum
(
*
(
const
unsigned
char
*
)
src
)
||
strchr
(
dont_escape
,
*
(
const
unsigned
char
*
)
src
)
!=
NULL
)
{
*
dst
=
*
src
;
}
else
if
(
dst
+
2
<
end
)
{
dst
[
0
]
=
'%'
;
dst
[
1
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
>>
4
];
dst
[
2
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
&
0xf
];
dst
+=
2
;
}
}
*
dst
=
'\0'
;
}
mongoose.c
View file @
37751a28
...
@@ -399,7 +399,7 @@ struct socket {
...
@@ -399,7 +399,7 @@ struct socket {
// NOTE(lsm): this enum shoulds be in sync with the config_options.
// NOTE(lsm): this enum shoulds be in sync with the config_options.
enum
{
enum
{
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
CGI_EXTENSIONS
,
CGI_ENVIRONMENT
,
PUT_DELETE_PASSWORDS_FILE
,
CGI_INTERPRETER
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
THROTTLE
,
PROTECT_URI
,
AUTHENTICATION_DOMAIN
,
SSI_EXTENSIONS
,
ACCESS_LOG_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
ACCESS_LOG_FILE
,
ENABLE_DIRECTORY_LISTING
,
ERROR_LOG_FILE
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
GLOBAL_PASSWORDS_FILE
,
INDEX_FILES
,
ENABLE_KEEP_ALIVE
,
ACCESS_CONTROL_LIST
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
EXTRA_MIME_TYPES
,
LISTENING_PORTS
,
DOCUMENT_ROOT
,
SSL_CERTIFICATE
,
...
@@ -446,9 +446,6 @@ struct mg_connection {
...
@@ -446,9 +446,6 @@ struct mg_connection {
int
request_len
;
// Size of the request + headers in a buffer
int
request_len
;
// Size of the request + headers in a buffer
int
data_len
;
// Total size of data in a buffer
int
data_len
;
// Total size of data in a buffer
int
status_code
;
// HTTP reply status code, e.g. 200
int
status_code
;
// HTTP reply status code, e.g. 200
int
throttle
;
// Throttling, bytes/sec. <= 0 means no throttle
time_t
last_throttle_time
;
// Last time throttled data was sent
int64_t
last_throttle_bytes
;
// Bytes sent this second
};
};
// Directory entry
// Directory entry
...
@@ -749,6 +746,25 @@ static void remove_double_dots_and_double_slashes(char *s) {
...
@@ -749,6 +746,25 @@ static void remove_double_dots_and_double_slashes(char *s) {
*
p
=
'\0'
;
*
p
=
'\0'
;
}
}
void
mg_url_encode
(
const
char
*
src
,
char
*
dst
,
size_t
dst_len
)
{
static
const
char
*
dont_escape
=
"._-$,;~()"
;
static
const
char
*
hex
=
"0123456789abcdef"
;
const
char
*
end
=
dst
+
dst_len
-
1
;
for
(;
*
src
!=
'\0'
&&
dst
<
end
;
src
++
,
dst
++
)
{
if
(
isalnum
(
*
(
const
unsigned
char
*
)
src
)
||
strchr
(
dont_escape
,
*
(
const
unsigned
char
*
)
src
)
!=
NULL
)
{
*
dst
=
*
src
;
}
else
if
(
dst
+
2
<
end
)
{
dst
[
0
]
=
'%'
;
dst
[
1
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
>>
4
];
dst
[
2
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
&
0xf
];
dst
+=
2
;
}
}
*
dst
=
'\0'
;
}
static
const
char
*
month_names
[]
=
{
static
const
char
*
month_names
[]
=
{
"Jan"
,
"Feb"
,
"Mar"
,
"Apr"
,
"May"
,
"Jun"
,
"Jan"
,
"Feb"
,
"Mar"
,
"Apr"
,
"May"
,
"Jun"
,
...
@@ -2291,6 +2307,243 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
...
@@ -2291,6 +2307,243 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
vec
->
len
=
strlen
(
vec
->
ptr
);
vec
->
len
=
strlen
(
vec
->
ptr
);
}
}
static
void
print_dir_entry
(
const
struct
de
*
de
)
{
char
size
[
64
],
mod
[
64
],
href
[
PATH_MAX
*
3
];
const
char
*
slash
=
de
->
file
.
is_directory
?
"/"
:
""
;
if
(
de
->
file
.
is_directory
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%s"
,
"[DIRECTORY]"
);
}
else
{
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double. Sigh.
if
(
de
->
file
.
size
<
1024
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%d"
,
(
int
)
de
->
file
.
size
);
}
else
if
(
de
->
file
.
size
<
0x100000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fk"
,
(
double
)
de
->
file
.
size
/
1024
.
0
);
}
else
if
(
de
->
file
.
size
<
0x40000000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fM"
,
(
double
)
de
->
file
.
size
/
1048576
);
}
else
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fG"
,
(
double
)
de
->
file
.
size
/
1073741824
);
}
}
strftime
(
mod
,
sizeof
(
mod
),
"%d-%b-%Y %H:%M"
,
localtime
(
&
de
->
file
.
modification_time
));
mg_url_encode
(
de
->
file_name
,
href
,
sizeof
(
href
));
de
->
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
de
->
conn
,
"<tr><td><a href=
\"
%s%s%s
\"
>%s%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
de
->
conn
->
request_info
.
uri
,
href
,
slash
,
de
->
file_name
,
slash
,
mod
,
size
);
}
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static
int
WINCDECL
compare_dir_entries
(
const
void
*
p1
,
const
void
*
p2
)
{
const
struct
de
*
a
=
(
const
struct
de
*
)
p1
,
*
b
=
(
const
struct
de
*
)
p2
;
const
char
*
query_string
=
a
->
conn
->
request_info
.
query_string
;
int
cmp_result
=
0
;
if
(
query_string
==
NULL
)
{
query_string
=
"na"
;
}
if
(
a
->
file
.
is_directory
&&
!
b
->
file
.
is_directory
)
{
return
-
1
;
// Always put directories on top
}
else
if
(
!
a
->
file
.
is_directory
&&
b
->
file
.
is_directory
)
{
return
1
;
// Always put directories on top
}
else
if
(
*
query_string
==
'n'
)
{
cmp_result
=
strcmp
(
a
->
file_name
,
b
->
file_name
);
}
else
if
(
*
query_string
==
's'
)
{
cmp_result
=
a
->
file
.
size
==
b
->
file
.
size
?
0
:
a
->
file
.
size
>
b
->
file
.
size
?
1
:
-
1
;
}
else
if
(
*
query_string
==
'd'
)
{
cmp_result
=
a
->
file
.
modification_time
==
b
->
file
.
modification_time
?
0
:
a
->
file
.
modification_time
>
b
->
file
.
modification_time
?
1
:
-
1
;
}
return
query_string
[
1
]
==
'd'
?
-
cmp_result
:
cmp_result
;
}
static
int
must_hide_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
pw_pattern
=
"**"
PASSWORDS_FILE_NAME
"$"
;
const
char
*
pattern
=
conn
->
ctx
->
config
[
HIDE_FILES
];
return
match_prefix
(
pw_pattern
,
strlen
(
pw_pattern
),
path
)
>
0
||
(
pattern
!=
NULL
&&
match_prefix
(
pattern
,
strlen
(
pattern
),
path
)
>
0
);
}
static
int
scan_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
,
void
*
data
,
void
(
*
cb
)(
struct
de
*
,
void
*
))
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir and hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
)
||
must_hide_file
(
conn
,
dp
->
d_name
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
de
.
file_name
=
dp
->
d_name
;
cb
(
&
de
,
data
);
}
(
void
)
closedir
(
dirp
);
}
return
1
;
}
static
int
remove_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir, but show hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
if
(
de
.
file
.
modification_time
)
{
if
(
de
.
file
.
is_directory
)
{
remove_directory
(
conn
,
path
);
}
else
{
mg_remove
(
path
);
}
}
}
(
void
)
closedir
(
dirp
);
rmdir
(
dir
);
}
return
1
;
}
struct
dir_scan_data
{
struct
de
*
entries
;
int
num_entries
;
int
arr_size
;
};
// Behaves like realloc(), but frees original pointer on failure
static
void
*
realloc2
(
void
*
ptr
,
size_t
size
)
{
void
*
new_ptr
=
realloc
(
ptr
,
size
);
if
(
new_ptr
==
NULL
)
{
free
(
ptr
);
}
return
new_ptr
;
}
static
void
dir_scan_callback
(
struct
de
*
de
,
void
*
data
)
{
struct
dir_scan_data
*
dsd
=
(
struct
dir_scan_data
*
)
data
;
if
(
dsd
->
entries
==
NULL
||
dsd
->
num_entries
>=
dsd
->
arr_size
)
{
dsd
->
arr_size
*=
2
;
dsd
->
entries
=
(
struct
de
*
)
realloc2
(
dsd
->
entries
,
dsd
->
arr_size
*
sizeof
(
dsd
->
entries
[
0
]));
}
if
(
dsd
->
entries
==
NULL
)
{
// TODO(lsm): propagate an error to the caller
dsd
->
num_entries
=
0
;
}
else
{
dsd
->
entries
[
dsd
->
num_entries
].
file_name
=
mg_strdup
(
de
->
file_name
);
dsd
->
entries
[
dsd
->
num_entries
].
file
=
de
->
file
;
dsd
->
entries
[
dsd
->
num_entries
].
conn
=
de
->
conn
;
dsd
->
num_entries
++
;
}
}
static
void
handle_directory_request
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
int
i
,
sort_direction
;
struct
dir_scan_data
data
=
{
NULL
,
0
,
128
};
if
(
!
scan_directory
(
conn
,
dir
,
&
data
,
dir_scan_callback
))
{
send_http_error
(
conn
,
500
,
"Cannot open directory"
,
"Error: opendir(%s): %s"
,
dir
,
strerror
(
ERRNO
));
return
;
}
sort_direction
=
conn
->
request_info
.
query_string
!=
NULL
&&
conn
->
request_info
.
query_string
[
1
]
==
'd'
?
'a'
:
'd'
;
conn
->
must_close
=
1
;
mg_printf
(
conn
,
"%s"
,
"HTTP/1.1 200 OK
\r\n
"
"Transfer-Encoding: Chunked
\r\n
"
"Content-Type: text/html; charset=utf-8
\r\n\r\n
"
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=
\"
0
\"
>"
"<tr><th><a href=
\"
?n%c
\"
>Name</a></th>"
"<th><a href=
\"
?d%c
\"
>Modified</a></th>"
"<th><a href=
\"
?s%c
\"
>Size</a></th></tr>"
"<tr><td colspan=
\"
3
\"
><hr></td></tr>"
,
conn
->
request_info
.
uri
,
conn
->
request_info
.
uri
,
sort_direction
,
sort_direction
,
sort_direction
);
// Print first entry - link to a parent directory
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<tr><td><a href=
\"
%s%s
\"
>%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
conn
->
request_info
.
uri
,
".."
,
"Parent directory"
,
"-"
,
"-"
);
// Sort and print directory entries
qsort
(
data
.
entries
,
(
size_t
)
data
.
num_entries
,
sizeof
(
data
.
entries
[
0
]),
compare_dir_entries
);
for
(
i
=
0
;
i
<
data
.
num_entries
;
i
++
)
{
print_dir_entry
(
&
data
.
entries
[
i
]);
free
(
data
.
entries
[
i
].
file_name
);
}
free
(
data
.
entries
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
num_bytes_sent
+=
mg_write
(
conn
,
"0
\r\n\r\n
"
,
5
);
conn
->
status_code
=
200
;
}
// Return number of bytes left to read for this connection
// Return number of bytes left to read for this connection
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
static
int64_t
left_to_read
(
const
struct
mg_connection
*
conn
)
{
return
conn
->
content_len
+
conn
->
request_len
-
conn
->
num_bytes_read
;
return
conn
->
content_len
+
conn
->
request_len
-
conn
->
num_bytes_read
;
...
@@ -2546,41 +2799,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
...
@@ -2546,41 +2799,8 @@ int mg_read(struct mg_connection *conn, void *buf, int len) {
}
}
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
int
mg_write
(
struct
mg_connection
*
conn
,
const
void
*
buf
,
int
len
)
{
time_t
now
;
return
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
int64_t
n
,
total
,
allowed
;
if
(
conn
->
throttle
>
0
)
{
if
((
now
=
time
(
NULL
))
!=
conn
->
last_throttle_time
)
{
conn
->
last_throttle_time
=
now
;
conn
->
last_throttle_bytes
=
0
;
}
allowed
=
conn
->
throttle
-
conn
->
last_throttle_bytes
;
if
(
allowed
>
(
int64_t
)
len
)
{
allowed
=
len
;
}
if
((
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
==
allowed
)
{
buf
=
(
char
*
)
buf
+
total
;
conn
->
last_throttle_bytes
+=
total
;
while
(
total
<
(
int64_t
)
len
&&
conn
->
ctx
->
stop_flag
==
0
)
{
allowed
=
conn
->
throttle
>
(
int64_t
)
len
-
total
?
(
int64_t
)
len
-
total
:
conn
->
throttle
;
if
((
n
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
allowed
))
!=
allowed
)
{
break
;
}
sleep
(
1
);
conn
->
last_throttle_bytes
=
allowed
;
conn
->
last_throttle_time
=
time
(
NULL
);
buf
=
(
char
*
)
buf
+
n
;
total
+=
n
;
}
}
}
else
{
total
=
push
(
NULL
,
conn
->
client
.
sock
,
conn
->
ssl
,
(
const
char
*
)
buf
,
(
int64_t
)
len
);
(
int64_t
)
len
);
}
return
(
int
)
total
;
}
}
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
int
mg_url_decode
(
const
char
*
src
,
int
src_len
,
char
*
dst
,
...
@@ -2794,262 +3014,6 @@ static int get_request_len(const char *buf, int buf_len) {
...
@@ -2794,262 +3014,6 @@ static int get_request_len(const char *buf, int buf_len) {
return
0
;
return
0
;
}
}
void
mg_url_encode
(
const
char
*
src
,
char
*
dst
,
size_t
dst_len
)
{
static
const
char
*
dont_escape
=
"._-$,;~()"
;
static
const
char
*
hex
=
"0123456789abcdef"
;
const
char
*
end
=
dst
+
dst_len
-
1
;
for
(;
*
src
!=
'\0'
&&
dst
<
end
;
src
++
,
dst
++
)
{
if
(
isalnum
(
*
(
const
unsigned
char
*
)
src
)
||
strchr
(
dont_escape
,
*
(
const
unsigned
char
*
)
src
)
!=
NULL
)
{
*
dst
=
*
src
;
}
else
if
(
dst
+
2
<
end
)
{
dst
[
0
]
=
'%'
;
dst
[
1
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
>>
4
];
dst
[
2
]
=
hex
[(
*
(
const
unsigned
char
*
)
src
)
&
0xf
];
dst
+=
2
;
}
}
*
dst
=
'\0'
;
}
static
void
print_dir_entry
(
const
struct
de
*
de
)
{
char
size
[
64
],
mod
[
64
],
href
[
PATH_MAX
*
3
];
const
char
*
slash
=
de
->
file
.
is_directory
?
"/"
:
""
;
if
(
de
->
file
.
is_directory
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%s"
,
"[DIRECTORY]"
);
}
else
{
// We use (signed) cast below because MSVC 6 compiler cannot
// convert unsigned __int64 to double. Sigh.
if
(
de
->
file
.
size
<
1024
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%d"
,
(
int
)
de
->
file
.
size
);
}
else
if
(
de
->
file
.
size
<
0x100000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fk"
,
(
double
)
de
->
file
.
size
/
1024
.
0
);
}
else
if
(
de
->
file
.
size
<
0x40000000
)
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fM"
,
(
double
)
de
->
file
.
size
/
1048576
);
}
else
{
mg_snprintf
(
size
,
sizeof
(
size
),
"%.1fG"
,
(
double
)
de
->
file
.
size
/
1073741824
);
}
}
strftime
(
mod
,
sizeof
(
mod
),
"%d-%b-%Y %H:%M"
,
localtime
(
&
de
->
file
.
modification_time
));
mg_url_encode
(
de
->
file_name
,
href
,
sizeof
(
href
));
de
->
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
de
->
conn
,
"<tr><td><a href=
\"
%s%s%s
\"
>%s%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
de
->
conn
->
request_info
.
uri
,
href
,
slash
,
de
->
file_name
,
slash
,
mod
,
size
);
}
// This function is called from send_directory() and used for
// sorting directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static
int
WINCDECL
compare_dir_entries
(
const
void
*
p1
,
const
void
*
p2
)
{
const
struct
de
*
a
=
(
const
struct
de
*
)
p1
,
*
b
=
(
const
struct
de
*
)
p2
;
const
char
*
query_string
=
a
->
conn
->
request_info
.
query_string
;
int
cmp_result
=
0
;
if
(
query_string
==
NULL
)
{
query_string
=
"na"
;
}
if
(
a
->
file
.
is_directory
&&
!
b
->
file
.
is_directory
)
{
return
-
1
;
// Always put directories on top
}
else
if
(
!
a
->
file
.
is_directory
&&
b
->
file
.
is_directory
)
{
return
1
;
// Always put directories on top
}
else
if
(
*
query_string
==
'n'
)
{
cmp_result
=
strcmp
(
a
->
file_name
,
b
->
file_name
);
}
else
if
(
*
query_string
==
's'
)
{
cmp_result
=
a
->
file
.
size
==
b
->
file
.
size
?
0
:
a
->
file
.
size
>
b
->
file
.
size
?
1
:
-
1
;
}
else
if
(
*
query_string
==
'd'
)
{
cmp_result
=
a
->
file
.
modification_time
==
b
->
file
.
modification_time
?
0
:
a
->
file
.
modification_time
>
b
->
file
.
modification_time
?
1
:
-
1
;
}
return
query_string
[
1
]
==
'd'
?
-
cmp_result
:
cmp_result
;
}
static
int
must_hide_file
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
pw_pattern
=
"**"
PASSWORDS_FILE_NAME
"$"
;
const
char
*
pattern
=
conn
->
ctx
->
config
[
HIDE_FILES
];
return
match_prefix
(
pw_pattern
,
strlen
(
pw_pattern
),
path
)
>
0
||
(
pattern
!=
NULL
&&
match_prefix
(
pattern
,
strlen
(
pattern
),
path
)
>
0
);
}
static
int
scan_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
,
void
*
data
,
void
(
*
cb
)(
struct
de
*
,
void
*
))
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir and hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
)
||
must_hide_file
(
conn
,
dp
->
d_name
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
de
.
file_name
=
dp
->
d_name
;
cb
(
&
de
,
data
);
}
(
void
)
closedir
(
dirp
);
}
return
1
;
}
static
int
remove_directory
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
char
path
[
PATH_MAX
];
struct
dirent
*
dp
;
DIR
*
dirp
;
struct
de
de
;
if
((
dirp
=
opendir
(
dir
))
==
NULL
)
{
return
0
;
}
else
{
de
.
conn
=
conn
;
while
((
dp
=
readdir
(
dirp
))
!=
NULL
)
{
// Do not show current dir, but show hidden files
if
(
!
strcmp
(
dp
->
d_name
,
"."
)
||
!
strcmp
(
dp
->
d_name
,
".."
))
{
continue
;
}
mg_snprintf
(
path
,
sizeof
(
path
),
"%s%c%s"
,
dir
,
'/'
,
dp
->
d_name
);
// If we don't memset stat structure to zero, mtime will have
// garbage and strftime() will segfault later on in
// print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79
memset
(
&
de
.
file
,
0
,
sizeof
(
de
.
file
));
mg_stat
(
path
,
&
de
.
file
);
if
(
de
.
file
.
modification_time
)
{
if
(
de
.
file
.
is_directory
)
{
remove_directory
(
conn
,
path
);
}
else
{
mg_remove
(
path
);
}
}
}
(
void
)
closedir
(
dirp
);
rmdir
(
dir
);
}
return
1
;
}
struct
dir_scan_data
{
struct
de
*
entries
;
int
num_entries
;
int
arr_size
;
};
// Behaves like realloc(), but frees original pointer on failure
static
void
*
realloc2
(
void
*
ptr
,
size_t
size
)
{
void
*
new_ptr
=
realloc
(
ptr
,
size
);
if
(
new_ptr
==
NULL
)
{
free
(
ptr
);
}
return
new_ptr
;
}
static
void
dir_scan_callback
(
struct
de
*
de
,
void
*
data
)
{
struct
dir_scan_data
*
dsd
=
(
struct
dir_scan_data
*
)
data
;
if
(
dsd
->
entries
==
NULL
||
dsd
->
num_entries
>=
dsd
->
arr_size
)
{
dsd
->
arr_size
*=
2
;
dsd
->
entries
=
(
struct
de
*
)
realloc2
(
dsd
->
entries
,
dsd
->
arr_size
*
sizeof
(
dsd
->
entries
[
0
]));
}
if
(
dsd
->
entries
==
NULL
)
{
// TODO(lsm): propagate an error to the caller
dsd
->
num_entries
=
0
;
}
else
{
dsd
->
entries
[
dsd
->
num_entries
].
file_name
=
mg_strdup
(
de
->
file_name
);
dsd
->
entries
[
dsd
->
num_entries
].
file
=
de
->
file
;
dsd
->
entries
[
dsd
->
num_entries
].
conn
=
de
->
conn
;
dsd
->
num_entries
++
;
}
}
static
void
handle_directory_request
(
struct
mg_connection
*
conn
,
const
char
*
dir
)
{
int
i
,
sort_direction
;
struct
dir_scan_data
data
=
{
NULL
,
0
,
128
};
if
(
!
scan_directory
(
conn
,
dir
,
&
data
,
dir_scan_callback
))
{
send_http_error
(
conn
,
500
,
"Cannot open directory"
,
"Error: opendir(%s): %s"
,
dir
,
strerror
(
ERRNO
));
return
;
}
sort_direction
=
conn
->
request_info
.
query_string
!=
NULL
&&
conn
->
request_info
.
query_string
[
1
]
==
'd'
?
'a'
:
'd'
;
conn
->
must_close
=
1
;
mg_printf
(
conn
,
"%s"
,
"HTTP/1.1 200 OK
\r\n
"
"Transfer-Encoding: Chunked
\r\n
"
"Content-Type: text/html; charset=utf-8
\r\n\r\n
"
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<html><head><title>Index of %s</title>"
"<style>th {text-align: left;}</style></head>"
"<body><h1>Index of %s</h1><pre><table cellpadding=
\"
0
\"
>"
"<tr><th><a href=
\"
?n%c
\"
>Name</a></th>"
"<th><a href=
\"
?d%c
\"
>Modified</a></th>"
"<th><a href=
\"
?s%c
\"
>Size</a></th></tr>"
"<tr><td colspan=
\"
3
\"
><hr></td></tr>"
,
conn
->
request_info
.
uri
,
conn
->
request_info
.
uri
,
sort_direction
,
sort_direction
,
sort_direction
);
// Print first entry - link to a parent directory
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"<tr><td><a href=
\"
%s%s
\"
>%s</a></td>"
"<td> %s</td><td> %s</td></tr>
\n
"
,
conn
->
request_info
.
uri
,
".."
,
"Parent directory"
,
"-"
,
"-"
);
// Sort and print directory entries
qsort
(
data
.
entries
,
(
size_t
)
data
.
num_entries
,
sizeof
(
data
.
entries
[
0
]),
compare_dir_entries
);
for
(
i
=
0
;
i
<
data
.
num_entries
;
i
++
)
{
print_dir_entry
(
&
data
.
entries
[
i
]);
free
(
data
.
entries
[
i
].
file_name
);
}
free
(
data
.
entries
);
conn
->
num_bytes_sent
+=
mg_chunked_printf
(
conn
,
"%s"
,
"</table></body></html>"
);
conn
->
num_bytes_sent
+=
mg_write
(
conn
,
"0
\r\n\r\n
"
,
5
);
conn
->
status_code
=
200
;
}
// Send len bytes from the opened file to the client.
// Send len bytes from the opened file to the client.
static
void
send_file_data
(
struct
mg_connection
*
conn
,
FILE
*
fp
,
static
void
send_file_data
(
struct
mg_connection
*
conn
,
FILE
*
fp
,
int64_t
offset
,
int64_t
len
)
{
int64_t
offset
,
int64_t
len
)
{
...
@@ -4367,38 +4331,6 @@ static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
...
@@ -4367,38 +4331,6 @@ static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
return
len
;
return
len
;
}
}
static
int
set_throttle
(
const
char
*
spec
,
uint32_t
remote_ip
,
const
char
*
uri
)
{
int
throttle
=
0
;
struct
vec
vec
,
val
;
uint32_t
net
,
mask
;
char
mult
;
double
v
;
while
((
spec
=
next_option
(
spec
,
&
vec
,
&
val
))
!=
NULL
)
{
mult
=
','
;
if
(
sscanf
(
val
.
ptr
,
"%lf%c"
,
&
v
,
&
mult
)
<
1
||
v
<
0
||
(
lowercase
(
&
mult
)
!=
'k'
&&
lowercase
(
&
mult
)
!=
'm'
&&
mult
!=
','
))
{
continue
;
}
v
*=
lowercase
(
&
mult
)
==
'k'
?
1024
:
lowercase
(
&
mult
)
==
'm'
?
1048576
:
1
;
if
(
vec
.
len
==
1
&&
vec
.
ptr
[
0
]
==
'*'
)
{
throttle
=
(
int
)
v
;
}
else
if
(
parse_net
(
vec
.
ptr
,
&
net
,
&
mask
)
>
0
)
{
if
((
remote_ip
&
mask
)
==
net
)
{
throttle
=
(
int
)
v
;
}
}
else
if
(
match_prefix
(
vec
.
ptr
,
vec
.
len
,
uri
)
>
0
)
{
throttle
=
(
int
)
v
;
}
}
return
throttle
;
}
static
uint32_t
get_remote_ip
(
const
struct
mg_connection
*
conn
)
{
return
ntohl
(
*
(
uint32_t
*
)
&
conn
->
client
.
rsa
.
sin
.
sin_addr
);
}
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
FILE
*
mg_upload
(
struct
mg_connection
*
conn
,
const
char
*
destination_dir
,
char
*
path
,
int
path_len
)
{
char
*
path
,
int
path_len
)
{
const
char
*
content_type_header
,
*
boundary_start
;
const
char
*
content_type_header
,
*
boundary_start
;
...
@@ -4603,8 +4535,6 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -4603,8 +4535,6 @@ static void handle_request(struct mg_connection *conn) {
uri_len
=
(
int
)
strlen
(
ri
->
uri
);
uri_len
=
(
int
)
strlen
(
ri
->
uri
);
mg_url_decode
(
ri
->
uri
,
uri_len
,
(
char
*
)
ri
->
uri
,
uri_len
+
1
,
0
);
mg_url_decode
(
ri
->
uri
,
uri_len
,
(
char
*
)
ri
->
uri
,
uri_len
+
1
,
0
);
remove_double_dots_and_double_slashes
((
char
*
)
ri
->
uri
);
remove_double_dots_and_double_slashes
((
char
*
)
ri
->
uri
);
conn
->
throttle
=
set_throttle
(
conn
->
ctx
->
config
[
THROTTLE
],
get_remote_ip
(
conn
),
ri
->
uri
);
path
[
0
]
=
'\0'
;
path
[
0
]
=
'\0'
;
convert_uri_to_file_name
(
conn
,
path
,
sizeof
(
path
),
&
file
);
convert_uri_to_file_name
(
conn
,
path
,
sizeof
(
path
),
&
file
);
...
@@ -4898,7 +4828,7 @@ static void reset_per_request_attributes(struct mg_connection *conn) {
...
@@ -4898,7 +4828,7 @@ static void reset_per_request_attributes(struct mg_connection *conn) {
conn
->
path_info
=
NULL
;
conn
->
path_info
=
NULL
;
conn
->
num_bytes_sent
=
conn
->
num_bytes_read
=
0
;
conn
->
num_bytes_sent
=
conn
->
num_bytes_read
=
0
;
conn
->
status_code
=
-
1
;
conn
->
status_code
=
-
1
;
conn
->
must_close
=
conn
->
request_len
=
conn
->
throttle
=
0
;
conn
->
must_close
=
conn
->
request_len
=
0
;
}
}
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
static
void
close_socket_gracefully
(
struct
mg_connection
*
conn
)
{
...
...
test/unit_test.c
View file @
37751a28
...
@@ -416,19 +416,6 @@ static void test_mg_get_var(void) {
...
@@ -416,19 +416,6 @@ static void test_mg_get_var(void) {
ASSERT
(
mg_get_var
(
post
[
1
],
strlen
(
post
[
1
]),
"st"
,
buf
,
17
)
==
16
);
ASSERT
(
mg_get_var
(
post
[
1
],
strlen
(
post
[
1
]),
"st"
,
buf
,
17
)
==
16
);
}
}
static
void
test_set_throttle
(
void
)
{
ASSERT
(
set_throttle
(
NULL
,
0x0a000001
,
"/"
)
==
0
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=20"
,
0x0a000001
,
"/"
)
==
20
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=0.5k"
,
0x0a000001
,
"/"
)
==
512
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=17m"
,
0x0a000001
,
"/"
)
==
1048576
*
17
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=1x"
,
0x0a000001
,
"/"
)
==
0
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=5,0.0.0.0/0=10"
,
0x0a000001
,
"/"
)
==
10
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=5,/foo/**=7"
,
0x0a000001
,
"/index"
)
==
5
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=5,/foo/**=7"
,
0x0a000001
,
"/foo/x"
)
==
7
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=5,/foo/**=7"
,
0x0b000001
,
"/foxo/x"
)
==
0
);
ASSERT
(
set_throttle
(
"10.0.0.0/8=5,*=1"
,
0x0b000001
,
"/foxo/x"
)
==
1
);
}
static
void
test_next_option
(
void
)
{
static
void
test_next_option
(
void
)
{
const
char
*
p
,
*
list
=
"x/8,/y**=1;2k,z"
;
const
char
*
p
,
*
list
=
"x/8,/y**=1;2k,z"
;
struct
vec
a
,
b
;
struct
vec
a
,
b
;
...
@@ -684,7 +671,6 @@ int __cdecl main(void) {
...
@@ -684,7 +671,6 @@ int __cdecl main(void) {
test_should_keep_alive
();
test_should_keep_alive
();
test_mg_download
();
test_mg_download
();
test_mg_get_var
();
test_mg_get_var
();
test_set_throttle
();
test_next_option
();
test_next_option
();
test_mg_stat
();
test_mg_stat
();
test_skip_quoted
();
test_skip_quoted
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment