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
546bec33
Commit
546bec33
authored
Aug 29, 2010
by
valenok
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
API change for mg_start: most binary compatible across releases.
parent
0b1e621c
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
707 additions
and
719 deletions
+707
-719
main.c
main.c
+191
-217
mongoose.c
mongoose.c
+300
-254
mongoose.h
mongoose.h
+86
-79
embed.c
test/embed.c
+114
-138
test.pl
test/test.pl
+16
-31
No files found.
main.c
View file @
546bec33
...
@@ -57,239 +57,213 @@ static int exit_flag; /* Program termination flag */
...
@@ -57,239 +57,213 @@ static int exit_flag; /* Program termination flag */
#define CONFIG_FILE "mongoose.conf"
#define CONFIG_FILE "mongoose.conf"
#endif
/* !CONFIG_FILE */
#endif
/* !CONFIG_FILE */
static
void
#define MAX_OPTIONS 40
signal_handler
(
int
sig_num
)
{
static
void
signal_handler
(
int
sig_num
)
{
#if !defined(_WIN32)
#if !defined(_WIN32)
if
(
sig_num
==
SIGCHLD
)
{
if
(
sig_num
==
SIGCHLD
)
{
do
{
do
{
}
while
(
waitpid
(
-
1
,
&
sig_num
,
WNOHANG
)
>
0
);
}
while
(
waitpid
(
-
1
,
&
sig_num
,
WNOHANG
)
>
0
);
}
else
}
else
#endif
/* !_WIN32 */
#endif
/* !_WIN32 */
{
{
exit_flag
=
sig_num
;
exit_flag
=
sig_num
;
}
}
}
}
/*
/*
* Edit the passwords file.
* Edit the passwords file.
*/
*/
static
int
static
int
mg_edit_passwords
(
const
char
*
fname
,
const
char
*
domain
,
mg_edit_passwords
(
const
char
*
fname
,
const
char
*
domain
,
const
char
*
user
,
const
char
*
pass
)
{
const
char
*
user
,
const
char
*
pass
)
struct
mg_context
*
ctx
;
{
const
char
*
options
[]
=
{
"authentication_domain"
,
NULL
,
NULL
};
struct
mg_context
*
ctx
;
int
success
;
struct
mg_config
config
;
int
retval
;
options
[
1
]
=
domain
;
ctx
=
mg_start
(
NULL
,
options
);
memset
(
&
config
,
0
,
sizeof
(
config
));
success
=
mg_modify_passwords_file
(
ctx
,
fname
,
user
,
pass
);
config
.
auth_domain
=
(
char
*
)
domain
;
mg_stop
(
ctx
);
config
.
num_threads
=
"0"
;
config
.
listening_ports
=
""
;
return
success
;
ctx
=
mg_start
(
&
config
);
retval
=
mg_modify_passwords_file
(
ctx
,
fname
,
user
,
pass
);
mg_stop
(
ctx
);
return
(
retval
);
}
}
#define OFFSET(x) offsetof(struct mg_config, x)
static
void
show_usage_and_exit
(
void
)
{
const
char
**
names
;
static
struct
option_descriptor
{
int
i
,
len
;
const
char
*
name
;
const
char
*
description
;
fprintf
(
stderr
,
"Mongoose version %s (c) Sergey Lyubka
\n
"
,
mg_version
());
size_t
offset
;
fprintf
(
stderr
,
"Usage:
\n
"
);
}
known_options
[]
=
{
fprintf
(
stderr
,
" mongoose -A <htpasswd_file> <realm> <user> <passwd>
\n
"
);
{
"root"
,
"
\t
Web root directory"
,
OFFSET
(
document_root
)},
fprintf
(
stderr
,
" mongoose <config_file>
\n
"
);
{
"index_files"
,
"Index files"
,
OFFSET
(
index_files
)},
fprintf
(
stderr
,
" mongoose [-option value ...]
\n
"
);
{
"ssl_cert"
,
"SSL certificate file"
,
OFFSET
(
ssl_certificate
)},
fprintf
(
stderr
,
"OPTIONS:
\n
"
);
{
"ports"
,
"Listening ports"
,
OFFSET
(
listening_ports
)},
{
"dir_list"
,
"Directory listing"
,
OFFSET
(
enable_directory_listing
)},
names
=
mg_get_valid_option_names
();
{
"protect"
,
"URI to htpasswd mapping"
,
OFFSET
(
protect
)},
len
=
2
;
{
"cgi_ext"
,
"CGI extensions"
,
OFFSET
(
cgi_extensions
)},
for
(
i
=
0
;
names
[
i
]
!=
NULL
;
i
++
)
{
{
"cgi_interp"
,
"CGI interpreter to use"
,
OFFSET
(
cgi_interpreter
)},
len
+=
strlen
(
names
[
i
])
+
1
;
{
"cgi_env"
,
"Custom CGI enviroment variables"
,
OFFSET
(
cgi_environment
)},
if
(
len
>=
80
)
{
{
"ssi_ext"
,
"SSI extensions"
,
OFFSET
(
ssi_extensions
)},
len
=
strlen
(
names
[
i
])
+
3
;
{
"auth_realm"
,
"Authentication domain name"
,
OFFSET
(
auth_domain
)},
fprintf
(
stderr
,
"%s"
,
"
\n
"
);
{
"auth_gpass"
,
"Global passwords file"
,
OFFSET
(
global_passwords_file
)},
}
{
"auth_PUT"
,
"PUT,DELETE auth file"
,
OFFSET
(
put_delete_passwords_file
)},
fprintf
(
stderr
,
"%s%c"
,
names
[
i
],
names
[
i
+
1
]
==
NULL
?
'\n'
:
','
);
{
"uid"
,
"
\t
Run as user"
,
OFFSET
(
uid
)},
}
{
"access_log"
,
"Access log file"
,
OFFSET
(
access_log_file
)},
fprintf
(
stderr
,
"See http://code.google.com/p/mongoose/wiki/MongooseManual"
{
"error_log"
,
"Error log file"
,
OFFSET
(
error_log_file
)},
" for more details.
\n
"
);
{
"acl"
,
"
\t
Allow/deny IP addresses/subnets"
,
OFFSET
(
acl
)},
fprintf
(
stderr
,
"Example:
\n
mongoose -listening_ports 80,443s "
{
"num_threads"
,
"Threads to spawn"
,
OFFSET
(
num_threads
)},
"-enable_directory_listing no
\n
"
);
{
"mime_types"
,
"Extra mime types to use"
,
OFFSET
(
mime_types
)},
{
NULL
,
NULL
,
0
}
exit
(
EXIT_FAILURE
);
};
static
void
show_usage_and_exit
(
const
struct
mg_config
*
config
)
{
const
struct
option_descriptor
*
o
;
const
char
*
value
;
(
void
)
fprintf
(
stderr
,
"Mongoose version %s (c) Sergey Lyubka
\n
"
"usage: mongoose [options] [config_file]
\n
"
,
mg_version
());
fprintf
(
stderr
,
" -A <htpasswd_file> <realm> <user> <passwd>
\n
"
);
for
(
o
=
known_options
;
o
->
name
!=
NULL
;
o
++
)
{
(
void
)
fprintf
(
stderr
,
" -%s
\t
%s"
,
o
->
name
,
o
->
description
);
value
=
*
(
char
**
)
((
char
*
)
config
+
o
->
offset
);
if
(
value
!=
NULL
)
fprintf
(
stderr
,
" (default:
\"
%s
\"
)"
,
value
);
fputc
(
'\n'
,
stderr
);
}
exit
(
EXIT_FAILURE
);
}
}
static
void
static
char
*
sdup
(
const
char
*
str
)
{
set_option
(
struct
mg_config
*
config
,
const
char
*
name
,
char
*
value
)
char
*
p
;
{
if
((
p
=
malloc
(
strlen
(
str
)
+
1
))
!=
NULL
)
{
const
struct
option_descriptor
*
o
;
strcpy
(
p
,
str
);
}
for
(
o
=
known_options
;
o
->
name
!=
NULL
;
o
++
)
return
p
;
if
(
strcmp
(
name
,
o
->
name
)
==
0
)
{
}
*
(
char
**
)
((
char
*
)
config
+
o
->
offset
)
=
value
;
break
;
}
if
(
o
->
name
==
NULL
)
static
void
set_option
(
char
**
options
,
const
char
*
name
,
const
char
*
value
)
{
show_usage_and_exit
(
config
);
int
i
;
for
(
i
=
0
;
i
<
MAX_OPTIONS
-
3
;
i
++
)
{
if
(
options
[
i
]
==
NULL
)
{
options
[
i
]
=
sdup
(
name
);
options
[
i
+
1
]
=
sdup
(
value
);
options
[
i
+
2
]
=
NULL
;
break
;
}
}
if
(
i
==
MAX_OPTIONS
-
3
)
{
fprintf
(
stderr
,
"%s
\n
"
,
"Too many options specified"
);
exit
(
EXIT_FAILURE
);
}
}
}
static
void
static
void
process_command_line_arguments
(
char
*
argv
[],
char
**
options
)
{
process_command_line_arguments
(
struct
mg_config
*
config
,
char
*
argv
[])
const
char
*
config_file
=
CONFIG_FILE
;
{
char
line
[
512
],
opt
[
512
],
*
vals
[
100
],
val
[
512
],
path
[
FILENAME_MAX
],
*
p
;
const
char
*
config_file
=
CONFIG_FILE
;
FILE
*
fp
;
char
line
[
512
],
opt
[
512
],
*
vals
[
100
],
size_t
i
,
line_no
=
0
;
val
[
512
],
path
[
FILENAME_MAX
],
*
p
;
FILE
*
fp
;
/* First find out, which config file to open */
size_t
i
,
line_no
=
0
;
for
(
i
=
1
;
argv
[
i
]
!=
NULL
&&
argv
[
i
][
0
]
==
'-'
;
i
+=
2
)
if
(
argv
[
i
+
1
]
==
NULL
)
/* First find out, which config file to open */
show_usage_and_exit
();
for
(
i
=
1
;
argv
[
i
]
!=
NULL
&&
argv
[
i
][
0
]
==
'-'
;
i
+=
2
)
if
(
argv
[
i
+
1
]
==
NULL
)
if
(
argv
[
i
]
!=
NULL
&&
argv
[
i
+
1
]
!=
NULL
)
{
show_usage_and_exit
(
config
);
/* More than one non-option arguments are given */
show_usage_and_exit
();
if
(
argv
[
i
]
!=
NULL
&&
argv
[
i
+
1
]
!=
NULL
)
{
}
else
if
(
argv
[
i
]
!=
NULL
)
{
/* More than one non-option arguments are given */
/* Just one non-option argument is given, this is config file */
show_usage_and_exit
(
config
);
config_file
=
argv
[
i
];
}
else
if
(
argv
[
i
]
!=
NULL
)
{
}
else
{
/* Just one non-option argument is given, this is config file */
/* No config file specified. Look for one where binary lives */
config_file
=
argv
[
i
];
if
((
p
=
strrchr
(
argv
[
0
],
DIRSEP
))
!=
0
)
{
}
else
{
snprintf
(
path
,
sizeof
(
path
),
"%.*s%s"
,
/* No config file specified. Look for one where binary lives */
(
int
)
(
p
-
argv
[
0
])
+
1
,
argv
[
0
],
config_file
);
if
((
p
=
strrchr
(
argv
[
0
],
DIRSEP
))
!=
0
)
{
config_file
=
path
;
(
void
)
snprintf
(
path
,
sizeof
(
path
),
"%.*s%s"
,
}
(
int
)
(
p
-
argv
[
0
])
+
1
,
argv
[
0
],
config_file
);
}
config_file
=
path
;
}
fp
=
fopen
(
config_file
,
"r"
);
}
/* If config file was set in command line and open failed, exit */
fp
=
fopen
(
config_file
,
"r"
);
if
(
fp
==
NULL
&&
argv
[
i
]
!=
NULL
)
{
fprintf
(
stderr
,
"cannot open config file %s: %s
\n
"
,
/* If config file was set in command line and open failed, exit */
config_file
,
strerror
(
errno
));
if
(
fp
==
NULL
&&
argv
[
i
]
!=
NULL
)
{
exit
(
EXIT_FAILURE
);
(
void
)
fprintf
(
stderr
,
"cannot open config file %s: %s
\n
"
,
}
config_file
,
strerror
(
errno
));
exit
(
EXIT_FAILURE
);
/* Reset temporary value holders */
}
(
void
)
memset
(
vals
,
0
,
sizeof
(
vals
));
/* Reset temporary value holders */
if
(
fp
!=
NULL
)
{
(
void
)
memset
(
vals
,
0
,
sizeof
(
vals
));
printf
(
"Loading config file %s, ignoring command line arguments
\n
"
,
config_file
);
if
(
fp
!=
NULL
)
{
(
void
)
printf
(
"Loading config file %s, "
/* Loop over the lines in config file */
"ignoring command line arguments
\n
"
,
config_file
);
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
/* Loop over the lines in config file */
line_no
++
;
while
(
fgets
(
line
,
sizeof
(
line
),
fp
)
!=
NULL
)
{
/* Ignore empty lines and comments */
line_no
++
;
if
(
line
[
0
]
==
'#'
||
line
[
0
]
==
'\n'
)
continue
;
/* Ignore empty lines and comments */
if
(
line
[
0
]
==
'#'
||
line
[
0
]
==
'\n'
)
if
(
sscanf
(
line
,
"%s %[^
\r\n
#]"
,
opt
,
val
)
!=
2
)
{
continue
;
fprintf
(
stderr
,
"%s: line %d is invalid
\n
"
,
config_file
,
(
int
)
line_no
);
if
(
sscanf
(
line
,
"%s %[^
\r\n
#]"
,
opt
,
val
)
!=
2
)
{
exit
(
EXIT_FAILURE
);
fprintf
(
stderr
,
"%s: line %d is invalid
\n
"
,
}
config_file
,
(
int
)
line_no
);
set_option
(
options
,
opt
,
val
);
exit
(
EXIT_FAILURE
);
}
}
/* TODO(lsm): free this at some point */
(
void
)
fclose
(
fp
);
p
=
malloc
(
strlen
(
val
)
+
1
);
}
else
{
(
void
)
strcpy
(
p
,
val
);
for
(
i
=
1
;
argv
[
i
]
!=
NULL
&&
argv
[
i
][
0
]
==
'-'
;
i
+=
2
)
set_option
(
config
,
opt
,
p
);
set_option
(
options
,
&
argv
[
i
][
1
],
argv
[
i
+
1
]);
}
}
(
void
)
fclose
(
fp
);
}
else
{
for
(
i
=
1
;
argv
[
i
]
!=
NULL
&&
argv
[
i
][
0
]
==
'-'
;
i
+=
2
)
set_option
(
config
,
&
argv
[
i
][
1
],
argv
[
i
+
1
]);
}
}
}
int
int
main
(
int
argc
,
char
*
argv
[])
{
main
(
int
argc
,
char
*
argv
[])
struct
mg_context
*
ctx
;
{
char
*
options
[
MAX_OPTIONS
];
struct
mg_config
config
;
int
i
;
struct
mg_context
*
ctx
;
/* Edit passwords file if -A option is specified */
/* Initialize configuration with default values */
if
(
argc
>
1
&&
argv
[
1
][
0
]
==
'-'
&&
argv
[
1
][
1
]
==
'A'
)
{
(
void
)
memset
(
&
config
,
0
,
sizeof
(
config
));
if
(
argc
!=
6
)
{
config
.
document_root
=
"."
;
show_usage_and_exit
();
config
.
enable_directory_listing
=
"yes"
;
}
config
.
auth_domain
=
"mydomain.com"
;
exit
(
mg_edit_passwords
(
argv
[
2
],
argv
[
3
],
argv
[
4
],
argv
[
5
])
?
config
.
num_threads
=
"20"
;
EXIT_SUCCESS
:
EXIT_FAILURE
);
config
.
index_files
=
"index.html,index.htm,index.cgi"
;
}
config
.
cgi_extensions
=
".cgi,.pl,.php"
;
config
.
ssi_extensions
=
".shtml,.shtm"
;
/* Show usage if -h or --help options are specified */
config
.
listening_ports
=
"8080"
;
if
(
argc
==
2
&&
(
!
strcmp
(
argv
[
1
],
"-h"
)
||
!
strcmp
(
argv
[
1
],
"--help"
)))
{
show_usage_and_exit
();
/* Edit passwords file if -A option is specified */
}
if
(
argc
>
1
&&
argv
[
1
][
0
]
==
'-'
&&
argv
[
1
][
1
]
==
'A'
)
{
if
(
argc
!=
6
)
/* Update config based on command line arguments */
show_usage_and_exit
(
&
config
);
options
[
0
]
=
NULL
;
exit
(
mg_edit_passwords
(
argv
[
2
],
argv
[
3
],
argv
[
4
],
argv
[
5
])
==
process_command_line_arguments
(
argv
,
options
);
MG_SUCCESS
?
EXIT_SUCCESS
:
EXIT_FAILURE
);
}
/* Setup signal handler: quit on Ctrl-C */
/* Show usage if -h or --help options are specified */
if
(
argc
==
2
&&
(
!
strcmp
(
argv
[
1
],
"-h"
)
||
!
strcmp
(
argv
[
1
],
"--help"
)))
show_usage_and_exit
(
&
config
);
/* Update config based on command line arguments */
process_command_line_arguments
(
&
config
,
argv
);
/* Setup signal handler: quit on Ctrl-C */
#ifndef _WIN32
#ifndef _WIN32
(
void
)
signal
(
SIGCHLD
,
signal_handler
);
signal
(
SIGCHLD
,
signal_handler
);
#endif
/* _WIN32 */
#endif
/* _WIN32 */
(
void
)
signal
(
SIGTERM
,
signal_handler
);
signal
(
SIGTERM
,
signal_handler
);
(
void
)
signal
(
SIGINT
,
signal_handler
);
signal
(
SIGINT
,
signal_handler
);
/* Start Mongoose */
/* Start Mongoose */
if
((
ctx
=
mg_start
(
&
config
))
==
NULL
)
{
ctx
=
mg_start
(
NULL
,
(
const
char
**
)
options
);
(
void
)
printf
(
"%s
\n
"
,
"Cannot initialize Mongoose context"
);
for
(
i
=
0
;
options
[
i
]
!=
NULL
;
i
++
)
{
exit
(
EXIT_FAILURE
);
free
(
options
[
i
]);
}
}
(
void
)
printf
(
"Mongoose %s started on port(s) %s "
if
(
ctx
==
NULL
)
{
"with web root [%s]
\n
"
,
(
void
)
printf
(
"%s
\n
"
,
"Cannot initialize Mongoose context"
);
mg_version
(),
config
.
listening_ports
,
config
.
document_root
);
exit
(
EXIT_FAILURE
);
}
fflush
(
stdout
);
while
(
exit_flag
==
0
)
printf
(
"Mongoose %s started on port(s) %s with web root [%s]
\n
"
,
sleep
(
1
);
mg_version
(),
mg_get_option
(
ctx
,
"listening_ports"
),
mg_get_option
(
ctx
,
"document_root"
));
(
void
)
printf
(
"Exiting on signal %d, "
"waiting for all threads to finish..."
,
exit_flag
);
fflush
(
stdout
);
fflush
(
stdout
);
while
(
exit_flag
==
0
)
{
mg_stop
(
ctx
);
sleep
(
1
);
(
void
)
printf
(
"%s"
,
" done.
\n
"
);
}
return
(
EXIT_SUCCESS
);
printf
(
"Exiting on signal %d, waiting for all threads to finish..."
,
exit_flag
);
fflush
(
stdout
);
mg_stop
(
ctx
);
printf
(
"%s"
,
" done.
\n
"
);
return
EXIT_SUCCESS
;
}
}
mongoose.c
View file @
546bec33
...
@@ -222,7 +222,6 @@ typedef int SOCKET;
...
@@ -222,7 +222,6 @@ typedef int SOCKET;
typedef
int
socklen_t
;
typedef
int
socklen_t
;
#endif // NO_SOCKLEN_T
#endif // NO_SOCKLEN_T
typedef
enum
{
MG_FALSE
,
MG_TRUE
}
bool_t
;
typedef
void
*
(
*
mg_thread_func_t
)(
void
*
);
typedef
void
*
(
*
mg_thread_func_t
)(
void
*
);
static
const
char
*
http_500_error
=
"Internal Server Error"
;
static
const
char
*
http_500_error
=
"Internal Server Error"
;
...
@@ -333,9 +332,9 @@ struct vec {
...
@@ -333,9 +332,9 @@ struct vec {
// Structure used by mg_stat() function. Uses 64 bit file length.
// Structure used by mg_stat() function. Uses 64 bit file length.
struct
mgstat
{
struct
mgstat
{
bool_
t
is_directory
;
// Directory marker
in
t
is_directory
;
// Directory marker
int64_t
size
;
// File size
int64_t
size
;
// File size
time_t
mtime
;
// Modification time
time_t
mtime
;
// Modification time
};
};
// Describes listening socket, or socket which was accept()-ed by the master
// Describes listening socket, or socket which was accept()-ed by the master
...
@@ -345,19 +344,39 @@ struct socket {
...
@@ -345,19 +344,39 @@ struct socket {
SOCKET
sock
;
// Listening socket
SOCKET
sock
;
// Listening socket
struct
usa
lsa
;
// Local socket address
struct
usa
lsa
;
// Local socket address
struct
usa
rsa
;
// Remote socket address
struct
usa
rsa
;
// Remote socket address
bool_t
is_ssl
;
// Is socket SSL-ed
int
is_ssl
;
// Is socket SSL-ed
};
enum
{
DOCUMENT_ROOT
,
LISTENING_PORTS
,
INDEX_FILES
,
SSL_CERTIFICATE
,
CGI_EXTENSIONS
,
CGI_INTERPRETER
,
CGI_ENVIRONMENT
,
SSI_EXTENSIONS
,
AUTHENTICATION_DOMAIN
,
URI_PROTECTION
,
GLOBAL_PASSWORDS_FILE
,
PUT_DELETE_PASSWORDS_FILE
,
ACCESS_LOG_FILE
,
ERROR_LOG_FILE
,
ACCESS_CONTROL_LIST
,
RUN_AS_USER
,
EXTRA_MIME_TYPES
,
ENABLE_DIRECTORY_LISTING
,
ENABLE_KEEP_ALIVE
,
NUM_THREADS
,
NUM_OPTIONS
};
static
const
char
*
option_names
[]
=
{
"document_root"
,
"listening_ports"
,
"index_files"
,
"ssl_certificate"
,
"cgi_extensions"
,
"cgi_interpreter"
,
"cgi_environment"
,
"ssi_extensions"
,
"authentication_domain"
,
"protect_uri"
,
"global_passwords_file"
,
"put_delete_passwords_file"
,
"access_log_file"
,
"error_log_file"
,
"access_control_list"
,
"run_as_user"
,
"extra_mime_types"
,
"enable_directory_listing"
,
"enable_keep_alive"
,
"num_threads"
,
NULL
};
};
struct
mg_context
{
struct
mg_context
{
int
stop_flag
;
// Should we stop event loop
int
stop_flag
;
// Should we stop event loop
SSL_CTX
*
ssl_ctx
;
// SSL context
SSL_CTX
*
ssl_ctx
;
// SSL context
const
struct
mg_config
*
config
;
// Mongoose configuration
char
*
config
[
NUM_OPTIONS
];
// Mongoose configuration parameters
mg_callback_t
user_callback
;
// User-defined callback function
struct
socket
*
listening_sockets
;
struct
socket
*
listening_sockets
;
int
num_threads
;
// Number of threads
int
num_threads
;
// Number of threads
pthread_mutex_t
mutex
;
// Protects (max|num)_threads
pthread_mutex_t
mutex
;
// Protects (max|num)_threads
pthread_cond_t
cond
;
// Condvar for tracking workers terminations
pthread_cond_t
cond
;
// Condvar for tracking workers terminations
struct
socket
queue
[
20
];
// Accepted sockets
struct
socket
queue
[
20
];
// Accepted sockets
int
sq_head
;
// Head of the socket queue
int
sq_head
;
// Head of the socket queue
...
@@ -380,11 +399,39 @@ struct mg_connection {
...
@@ -380,11 +399,39 @@ struct mg_connection {
int
data_len
;
// Total size of data in a buffer
int
data_len
;
// Total size of data in a buffer
};
};
const
char
**
mg_get_valid_option_names
(
void
)
{
return
option_names
;
}
static
void
*
call_user
(
struct
mg_connection
*
conn
,
enum
mg_event
event
)
{
return
conn
->
ctx
->
user_callback
==
NULL
?
NULL
:
conn
->
ctx
->
user_callback
(
event
,
conn
,
&
conn
->
request_info
);
}
static
int
get_option_index
(
const
char
*
name
)
{
int
i
;
for
(
i
=
0
;
option_names
[
i
]
!=
NULL
;
i
++
)
{
if
(
strcmp
(
option_names
[
i
],
name
)
==
0
)
{
return
i
;
}
}
return
-
1
;
}
const
char
*
mg_get_option
(
const
struct
mg_context
*
ctx
,
const
char
*
name
)
{
int
i
;
if
((
i
=
get_option_index
(
name
))
==
-
1
)
{
return
NULL
;
}
else
if
(
ctx
->
config
[
i
]
==
NULL
)
{
return
""
;
}
else
{
return
ctx
->
config
[
i
];
}
}
// Print error message to the opened error log stream.
// Print error message to the opened error log stream.
static
void
cry
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
static
void
cry
(
struct
mg_connection
*
conn
,
const
char
*
fmt
,
...)
{
char
buf
[
BUFSIZ
];
char
buf
[
BUFSIZ
];
mg_callback_t
log_callback
;
enum
mg_error_t
processed
=
MG_ERROR
;
va_list
ap
;
va_list
ap
;
FILE
*
fp
;
FILE
*
fp
;
time_t
timestamp
;
time_t
timestamp
;
...
@@ -396,14 +443,10 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -396,14 +443,10 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
// Do not lock when getting the callback value, here and below.
// Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the
// I suppose this is fine, since function cannot disappear in the
// same way string option can.
// same way string option can.
log_callback
=
conn
->
ctx
->
config
->
event_log_handler
;
conn
->
request_info
.
log_message
=
buf
;
conn
->
request_info
.
log_message
=
buf
;
if
(
log_callback
!=
NULL
)
{
if
(
call_user
(
conn
,
MG_EVENT_LOG
)
==
NULL
)
{
processed
=
log_callback
(
conn
,
&
conn
->
request_info
);
fp
=
conn
->
ctx
->
config
[
ERROR_LOG_FILE
]
==
NULL
?
stderr
:
}
mg_fopen
(
conn
->
ctx
->
config
[
ERROR_LOG_FILE
],
"a+"
);
if
(
processed
==
MG_ERROR
)
{
fp
=
conn
->
ctx
->
config
->
error_log_file
==
NULL
?
stderr
:
mg_fopen
(
conn
->
ctx
->
config
->
error_log_file
,
"a+"
);
if
(
fp
!=
NULL
)
{
if
(
fp
!=
NULL
)
{
flockfile
(
fp
);
flockfile
(
fp
);
...
@@ -611,7 +654,7 @@ static const char *next_option(const char *list, struct vec *val,
...
@@ -611,7 +654,7 @@ static const char *next_option(const char *list, struct vec *val,
}
}
#if !defined(NO_CGI)
#if !defined(NO_CGI)
static
bool_
t
match_extension
(
const
char
*
path
,
const
char
*
ext_list
)
{
static
in
t
match_extension
(
const
char
*
path
,
const
char
*
ext_list
)
{
struct
vec
ext_vec
;
struct
vec
ext_vec
;
size_t
path_len
;
size_t
path_len
;
...
@@ -621,9 +664,9 @@ static bool_t match_extension(const char *path, const char *ext_list) {
...
@@ -621,9 +664,9 @@ static bool_t match_extension(const char *path, const char *ext_list) {
if
(
ext_vec
.
len
<
path_len
&&
if
(
ext_vec
.
len
<
path_len
&&
mg_strncasecmp
(
path
+
path_len
-
ext_vec
.
len
,
mg_strncasecmp
(
path
+
path_len
-
ext_vec
.
len
,
ext_vec
.
ptr
,
ext_vec
.
len
)
==
0
)
ext_vec
.
ptr
,
ext_vec
.
len
)
==
0
)
return
MG_TRUE
;
return
1
;
return
MG_FALSE
;
return
0
;
}
}
#endif // !NO_CGI
#endif // !NO_CGI
...
@@ -632,17 +675,11 @@ static void send_http_error(struct mg_connection *conn, int status,
...
@@ -632,17 +675,11 @@ static void send_http_error(struct mg_connection *conn, int status,
char
buf
[
BUFSIZ
];
char
buf
[
BUFSIZ
];
va_list
ap
;
va_list
ap
;
int
len
;
int
len
;
mg_callback_t
error_handler
;
bool_t
handled
;
DEBUG_TRACE
((
"%d %s"
,
status
,
reason
));
DEBUG_TRACE
((
"%d %s"
,
status
,
reason
));
conn
->
request_info
.
status_code
=
status
;
conn
->
request_info
.
status_code
=
status
;
error_handler
=
conn
->
ctx
->
config
->
http_error_handler
;
if
(
call_user
(
conn
,
MG_HTTP_ERROR
)
==
NULL
)
{
handled
=
error_handler
?
error_handler
(
conn
,
&
conn
->
request_info
)
:
MG_ERROR
;
if
(
handled
==
MG_ERROR
)
{
buf
[
0
]
=
'\0'
;
buf
[
0
]
=
'\0'
;
len
=
0
;
len
=
0
;
...
@@ -668,7 +705,7 @@ static void send_http_error(struct mg_connection *conn, int status,
...
@@ -668,7 +705,7 @@ static void send_http_error(struct mg_connection *conn, int status,
#ifdef _WIN32
#ifdef _WIN32
static
int
pthread_mutex_init
(
pthread_mutex_t
*
mutex
,
void
*
unused
)
{
static
int
pthread_mutex_init
(
pthread_mutex_t
*
mutex
,
void
*
unused
)
{
unused
=
NULL
;
unused
=
NULL
;
*
mutex
=
CreateMutex
(
NULL
,
MG_
FALSE
,
NULL
);
*
mutex
=
CreateMutex
(
NULL
,
FALSE
,
NULL
);
return
*
mutex
==
NULL
?
-
1
:
0
;
return
*
mutex
==
NULL
?
-
1
:
0
;
}
}
...
@@ -686,7 +723,7 @@ static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
...
@@ -686,7 +723,7 @@ static int pthread_mutex_unlock(pthread_mutex_t *mutex) {
static
int
pthread_cond_init
(
pthread_cond_t
*
cv
,
const
void
*
unused
)
{
static
int
pthread_cond_init
(
pthread_cond_t
*
cv
,
const
void
*
unused
)
{
unused
=
NULL
;
unused
=
NULL
;
*
cv
=
CreateEvent
(
NULL
,
MG_FALSE
,
MG_
FALSE
,
NULL
);
*
cv
=
CreateEvent
(
NULL
,
FALSE
,
FALSE
,
NULL
);
return
*
cv
==
NULL
?
-
1
:
0
;
return
*
cv
==
NULL
?
-
1
:
0
;
}
}
...
@@ -1021,9 +1058,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
...
@@ -1021,9 +1058,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
me
=
GetCurrentProcess
();
me
=
GetCurrentProcess
();
(
void
)
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fd_stdin
),
me
,
(
void
)
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fd_stdin
),
me
,
&
si
.
hStdInput
,
0
,
MG_
TRUE
,
DUPLICATE_SAME_ACCESS
);
&
si
.
hStdInput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
(
void
)
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fd_stdout
),
me
,
(
void
)
DuplicateHandle
(
me
,
(
HANDLE
)
_get_osfhandle
(
fd_stdout
),
me
,
&
si
.
hStdOutput
,
0
,
MG_
TRUE
,
DUPLICATE_SAME_ACCESS
);
&
si
.
hStdOutput
,
0
,
TRUE
,
DUPLICATE_SAME_ACCESS
);
// If CGI file is a script, try to read the interpreter line
// If CGI file is a script, try to read the interpreter line
interp
=
conn
->
ctx
->
config
->
cgi_interpreter
;
interp
=
conn
->
ctx
->
config
->
cgi_interpreter
;
...
@@ -1056,7 +1093,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
...
@@ -1056,7 +1093,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
change_slashes_to_backslashes
(
line
);
change_slashes_to_backslashes
(
line
);
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
DEBUG_TRACE
((
"Running [%s]"
,
cmdline
));
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
MG_
TRUE
,
if
(
CreateProcessA
(
NULL
,
cmdline
,
NULL
,
NULL
,
TRUE
,
CREATE_NEW_PROCESS_GROUP
,
envblk
,
line
,
&
si
,
&
pi
)
==
0
)
{
CREATE_NEW_PROCESS_GROUP
,
envblk
,
line
,
&
si
,
&
pi
)
==
0
)
{
cry
(
conn
,
"%s: CreateProcess(%s): %d"
,
cry
(
conn
,
"%s: CreateProcess(%s): %d"
,
__func__
,
cmdline
,
ERRNO
);
__func__
,
cmdline
,
ERRNO
);
...
@@ -1139,12 +1176,12 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
...
@@ -1139,12 +1176,12 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
}
else
if
(
dup2
(
fd_stdout
,
1
)
==
-
1
)
{
}
else
if
(
dup2
(
fd_stdout
,
1
)
==
-
1
)
{
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fd_stdout
,
strerror
(
ERRNO
));
cry
(
conn
,
"%s: dup2(%d, 1): %s"
,
__func__
,
fd_stdout
,
strerror
(
ERRNO
));
}
else
{
}
else
{
(
void
)
dup2
(
fd_stdout
,
2
);
//
(void) dup2(fd_stdout, 2);
(
void
)
close
(
fd_stdin
);
(
void
)
close
(
fd_stdin
);
(
void
)
close
(
fd_stdout
);
(
void
)
close
(
fd_stdout
);
// Execute CGI program. No need to lock: new process
// Execute CGI program. No need to lock: new process
interp
=
conn
->
ctx
->
config
->
cgi_interpreter
;
interp
=
conn
->
ctx
->
config
[
CGI_INTERPRETER
]
;
if
(
interp
==
NULL
)
{
if
(
interp
==
NULL
)
{
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
(
void
)
execle
(
prog
,
prog
,
NULL
,
envp
);
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
cry
(
conn
,
"%s: execle(%s): %s"
,
__func__
,
prog
,
strerror
(
ERRNO
));
...
@@ -1266,6 +1303,7 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) {
...
@@ -1266,6 +1303,7 @@ int mg_read(struct mg_connection *conn, void *buf, size_t len) {
if
(
n
<=
0
)
{
if
(
n
<=
0
)
{
break
;
break
;
}
}
buf
=
(
char
*
)
buf
+
n
;
conn
->
consumed_content
+=
n
;
conn
->
consumed_content
+=
n
;
nread
+=
n
;
nread
+=
n
;
len
-=
n
;
len
-=
n
;
...
@@ -1297,7 +1335,7 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
...
@@ -1297,7 +1335,7 @@ int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
// uses '+' as character for space, see RFC 1866 section 8.2.1
// uses '+' as character for space, see RFC 1866 section 8.2.1
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
// http://ftp.ics.uci.edu/pub/ietf/html/rfc1866.txt
static
size_t
url_decode
(
const
char
*
src
,
size_t
src_len
,
char
*
dst
,
static
size_t
url_decode
(
const
char
*
src
,
size_t
src_len
,
char
*
dst
,
size_t
dst_len
,
bool_
t
is_form_url_encoded
)
{
size_t
dst_len
,
in
t
is_form_url_encoded
)
{
size_t
i
,
j
;
size_t
i
,
j
;
int
a
,
b
;
int
a
,
b
;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
...
@@ -1326,72 +1364,58 @@ static size_t url_decode(const char *src, size_t src_len, char *dst,
...
@@ -1326,72 +1364,58 @@ static size_t url_decode(const char *src, size_t src_len, char *dst,
// It can be specified in query string, or in the POST data.
// It can be specified in query string, or in the POST data.
// Return NULL if the variable not found, or allocated 0-terminated value.
// Return NULL if the variable not found, or allocated 0-terminated value.
// It is caller's responsibility to free the returned value.
// It is caller's responsibility to free the returned value.
enum
mg_error_t
mg_get_var
(
const
char
*
buf
,
size_t
buf_len
,
int
mg_get_var
(
const
char
*
buf
,
size_t
buf_len
,
const
char
*
name
,
const
char
*
var_name
,
char
*
var_value
,
char
*
dst
,
size_t
dst_len
)
{
size_t
var_value_len
)
{
const
char
*
p
,
*
e
,
*
s
;
const
char
*
p
,
*
e
,
*
s
;
char
*
val
;
size_t
name_len
,
len
;
size_t
var_len
,
len
;
enum
mg_error_t
ret_val
;
var_len
=
strlen
(
var_
name
);
name_len
=
strlen
(
name
);
e
=
buf
+
buf_len
;
e
=
buf
+
buf_len
;
val
=
NULL
;
len
=
-
1
;
ret_val
=
MG_NOT_FOUND
;
dst
[
0
]
=
'\0'
;
var_value
[
0
]
=
'\0'
;
// buf is "var1=val1&var2=val2...". Find variable first
// buf is "var1=val1&var2=val2...". Find variable first
for
(
p
=
buf
;
p
+
var_len
<
e
;
p
++
)
for
(
p
=
buf
;
p
!=
NULL
&&
p
+
name_len
<
e
;
p
++
)
{
if
((
p
==
buf
||
p
[
-
1
]
==
'&'
)
&&
p
[
var
_len
]
==
'='
&&
if
((
p
==
buf
||
p
[
-
1
]
==
'&'
)
&&
p
[
name
_len
]
==
'='
&&
!
mg_strncasecmp
(
var_name
,
p
,
var
_len
))
{
!
mg_strncasecmp
(
name
,
p
,
name
_len
))
{
// Point p to variable value
// Point p to variable value
p
+=
var
_len
+
1
;
p
+=
name
_len
+
1
;
// Point s to the end of the value
// Point s to the end of the value
s
=
(
const
char
*
)
memchr
(
p
,
'&'
,
e
-
p
);
s
=
(
const
char
*
)
memchr
(
p
,
'&'
,
e
-
p
);
if
(
s
==
NULL
)
{
if
(
s
==
NULL
)
{
s
=
e
;
s
=
e
;
}
}
assert
(
s
>=
p
);
/* Try to allocate the buffer */
// Decode variable into destination buffer
len
=
s
-
p
;
if
((
size_t
)
(
s
-
p
)
<
dst_len
)
{
if
(
len
>=
var_value_len
)
{
len
=
url_decode
(
p
,
s
-
p
,
dst
,
dst_len
,
1
);
ret_val
=
MG_BUFFER_TOO_SMALL
;
}
else
{
url_decode
(
p
,
len
,
var_value
,
len
+
1
,
MG_TRUE
);
ret_val
=
MG_SUCCESS
;
}
}
break
;
break
;
}
}
}
return
ret_val
;
return
len
;
}
enum
mg_error_t
mg_get_qsvar
(
const
struct
mg_request_info
*
ri
,
const
char
*
var_name
,
char
*
var_value
,
size_t
var_value_len
)
{
return
ri
->
query_string
==
NULL
?
MG_NOT_FOUND
:
mg_get_var
(
ri
->
query_string
,
strlen
(
ri
->
query_string
),
var_name
,
var_value
,
var_value_len
);
}
}
enum
mg_error_t
mg_get_cookie
(
const
struct
mg_connection
*
conn
,
int
mg_get_cookie
(
const
struct
mg_connection
*
conn
,
const
char
*
cookie_name
,
const
char
*
cookie_name
,
char
*
dst
,
char
*
dst
,
size_t
dst_size
)
{
size_t
dst_size
)
{
const
char
*
s
,
*
p
,
*
end
;
const
char
*
s
,
*
p
,
*
end
;
int
len
;
int
name_len
,
len
=
-
1
;
dst
[
0
]
=
'\0'
;
dst
[
0
]
=
'\0'
;
if
((
s
=
mg_get_header
(
conn
,
"Cookie"
))
==
NULL
)
{
if
((
s
=
mg_get_header
(
conn
,
"Cookie"
))
==
NULL
)
{
return
MG_NOT_FOUND
;
return
0
;
}
}
len
=
strlen
(
cookie_name
);
name_
len
=
strlen
(
cookie_name
);
end
=
s
+
strlen
(
s
);
end
=
s
+
strlen
(
s
);
for
(;
(
s
=
strstr
(
s
,
cookie_name
))
!=
NULL
;
s
+=
len
)
for
(;
(
s
=
strstr
(
s
,
cookie_name
))
!=
NULL
;
s
+=
name_
len
)
if
(
s
[
len
]
==
'='
)
{
if
(
s
[
name_
len
]
==
'='
)
{
s
+=
len
+
1
;
s
+=
name_
len
+
1
;
if
((
p
=
strchr
(
s
,
' '
))
==
NULL
)
if
((
p
=
strchr
(
s
,
' '
))
==
NULL
)
p
=
end
;
p
=
end
;
if
(
p
[
-
1
]
==
';'
)
if
(
p
[
-
1
]
==
';'
)
...
@@ -1400,15 +1424,14 @@ enum mg_error_t mg_get_cookie(const struct mg_connection *conn,
...
@@ -1400,15 +1424,14 @@ enum mg_error_t mg_get_cookie(const struct mg_connection *conn,
s
++
;
s
++
;
p
--
;
p
--
;
}
}
if
((
size_t
)
(
p
-
s
)
>=
dst_size
)
{
if
((
size_t
)
(
p
-
s
)
<
dst_size
)
{
return
MG_BUFFER_TOO_SMALL
;
len
=
(
p
-
s
)
+
1
;
}
else
{
mg_strlcpy
(
dst
,
s
,
len
);
mg_strlcpy
(
dst
,
s
,
(
p
-
s
)
+
1
);
return
MG_SUCCESS
;
}
}
break
;
}
}
return
MG_NOT_FOUND
;
return
len
;
}
}
// Mongoose allows to specify multiple directories to serve,
// Mongoose allows to specify multiple directories to serve,
...
@@ -1422,7 +1445,7 @@ static int get_document_root(const struct mg_connection *conn,
...
@@ -1422,7 +1445,7 @@ static int get_document_root(const struct mg_connection *conn,
uri
=
conn
->
request_info
.
uri
;
uri
=
conn
->
request_info
.
uri
;
len_of_matched_uri
=
0
;
len_of_matched_uri
=
0
;
root
=
next_option
(
conn
->
ctx
->
config
->
document_root
,
document_root
,
NULL
);
root
=
next_option
(
conn
->
ctx
->
config
[
DOCUMENT_ROOT
]
,
document_root
,
NULL
);
while
((
root
=
next_option
(
root
,
&
uri_vec
,
&
path_vec
))
!=
NULL
)
{
while
((
root
=
next_option
(
root
,
&
uri_vec
,
&
path_vec
))
!=
NULL
)
{
if
(
memcmp
(
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
)
==
0
)
{
if
(
memcmp
(
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
)
==
0
)
{
...
@@ -1650,7 +1673,7 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
...
@@ -1650,7 +1673,7 @@ static void get_mime_type(struct mg_context *ctx, const char *path,
// Scan user-defined mime types first, in case user wants to
// Scan user-defined mime types first, in case user wants to
// override default mime types.
// override default mime types.
list
=
ctx
->
config
->
mime_types
;
list
=
ctx
->
config
[
EXTRA_MIME_TYPES
]
;
while
((
list
=
next_option
(
list
,
&
ext_vec
,
&
mime_vec
))
!=
NULL
)
{
while
((
list
=
next_option
(
list
,
&
ext_vec
,
&
mime_vec
))
!=
NULL
)
{
// ext now points to the path suffix
// ext now points to the path suffix
ext
=
path
+
path_len
-
ext_vec
.
len
;
ext
=
path
+
path_len
-
ext_vec
.
len
;
...
@@ -1896,10 +1919,9 @@ void mg_md5(char *buf, ...) {
...
@@ -1896,10 +1919,9 @@ void mg_md5(char *buf, ...) {
}
}
// Check the user's password, return 1 if OK
// Check the user's password, return 1 if OK
static
bool_t
check_password
(
const
char
*
method
,
const
char
*
ha1
,
static
int
check_password
(
const
char
*
method
,
const
char
*
ha1
,
const
char
*
uri
,
const
char
*
uri
,
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
nonce
,
const
char
*
nc
,
const
char
*
cnonce
,
const
char
*
cnonce
,
const
char
*
qop
,
const
char
*
qop
,
const
char
*
response
)
{
const
char
*
response
)
{
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
char
ha2
[
32
+
1
],
expected_response
[
32
+
1
];
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
// NOTE(lsm): due to a bug in MSIE, we do not compare the URI
...
@@ -1908,7 +1930,7 @@ static bool_t check_password(const char *method, const char *ha1,
...
@@ -1908,7 +1930,7 @@ static bool_t check_password(const char *method, const char *ha1,
strlen
(
response
)
!=
32
strlen
(
response
)
!=
32
// || now - strtoul(dig->nonce, NULL, 10) > 3600
// || now - strtoul(dig->nonce, NULL, 10) > 3600
)
{
)
{
return
MG_FALSE
;
return
0
;
}
}
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
mg_md5
(
ha2
,
method
,
":"
,
uri
,
NULL
);
...
@@ -1927,12 +1949,12 @@ static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
...
@@ -1927,12 +1949,12 @@ static FILE *open_auth_file(struct mg_connection *conn, const char *path) {
struct
mgstat
st
;
struct
mgstat
st
;
FILE
*
fp
;
FILE
*
fp
;
if
(
ctx
->
config
->
global_passwords_file
!=
NULL
)
{
if
(
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
]
!=
NULL
)
{
// Use global passwords file
// Use global passwords file
fp
=
mg_fopen
(
ctx
->
config
->
global_passwords_file
,
"r"
);
fp
=
mg_fopen
(
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
]
,
"r"
);
if
(
fp
==
NULL
)
if
(
fp
==
NULL
)
cry
(
fc
(
ctx
),
"fopen(%s): %s"
,
cry
(
fc
(
ctx
),
"fopen(%s): %s"
,
ctx
->
config
->
global_passwords_file
,
strerror
(
ERRNO
));
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
]
,
strerror
(
ERRNO
));
}
else
if
(
!
mg_stat
(
path
,
&
st
)
&&
st
.
is_directory
)
{
}
else
if
(
!
mg_stat
(
path
,
&
st
)
&&
st
.
is_directory
)
{
(
void
)
mg_snprintf
(
conn
,
name
,
sizeof
(
name
),
"%s%c%s"
,
(
void
)
mg_snprintf
(
conn
,
name
,
sizeof
(
name
),
"%s%c%s"
,
path
,
DIRSEP
,
PASSWORDS_FILE_NAME
);
path
,
DIRSEP
,
PASSWORDS_FILE_NAME
);
...
@@ -1955,14 +1977,14 @@ struct ah {
...
@@ -1955,14 +1977,14 @@ struct ah {
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
char
*
user
,
*
uri
,
*
cnonce
,
*
response
,
*
qop
,
*
nc
,
*
nonce
;
};
};
static
bool_
t
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
static
in
t
parse_auth_header
(
struct
mg_connection
*
conn
,
char
*
buf
,
size_t
buf_size
,
struct
ah
*
ah
)
{
size_t
buf_size
,
struct
ah
*
ah
)
{
char
*
name
,
*
value
,
*
s
;
char
*
name
,
*
value
,
*
s
;
const
char
*
auth_header
;
const
char
*
auth_header
;
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
if
((
auth_header
=
mg_get_header
(
conn
,
"Authorization"
))
==
NULL
||
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
mg_strncasecmp
(
auth_header
,
"Digest "
,
7
)
!=
0
)
{
return
MG_FALSE
;
return
0
;
}
}
// Make modifiable copy of the auth header
// Make modifiable copy of the auth header
...
@@ -2016,16 +2038,16 @@ static bool_t parse_auth_header(struct mg_connection *conn, char *buf,
...
@@ -2016,16 +2038,16 @@ static bool_t parse_auth_header(struct mg_connection *conn, char *buf,
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
conn
->
request_info
.
remote_user
=
mg_strdup
(
ah
->
user
);
}
}
return
MG_TRUE
;
return
1
;
}
}
// Authorize against the opened passwords file. Return 1 if authorized.
// Authorize against the opened passwords file. Return 1 if authorized.
static
bool_
t
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
static
in
t
authorize
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
struct
ah
ah
;
struct
ah
ah
;
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MAX_REQUEST_SIZE
];
char
line
[
256
],
f_user
[
256
],
ha1
[
256
],
f_domain
[
256
],
buf
[
MAX_REQUEST_SIZE
];
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
if
(
!
parse_auth_header
(
conn
,
buf
,
sizeof
(
buf
),
&
ah
))
{
return
MG_FALSE
;
return
0
;
}
}
// Loop over passwords file
// Loop over passwords file
...
@@ -2035,29 +2057,28 @@ static bool_t authorize(struct mg_connection *conn, FILE *fp) {
...
@@ -2035,29 +2057,28 @@ static bool_t authorize(struct mg_connection *conn, FILE *fp) {
}
}
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
if
(
!
strcmp
(
ah
.
user
,
f_user
)
&&
!
strcmp
(
conn
->
ctx
->
config
->
auth_domain
,
f_domain
))
!
strcmp
(
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
]
,
f_domain
))
return
check_password
(
return
check_password
(
conn
->
request_info
.
request_method
,
conn
->
request_info
.
request_method
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ha1
,
ah
.
uri
,
ah
.
nonce
,
ah
.
nc
,
ah
.
cnonce
,
ah
.
qop
,
ah
.
response
);
ah
.
response
);
}
}
return
MG_FALSE
;
return
0
;
}
}
// Return MG_TRUE if request is authorised, MG_FALSE otherwise.
// Return 1 if request is authorised, 0 otherwise.
static
bool_t
check_authorization
(
struct
mg_connection
*
conn
,
static
int
check_authorization
(
struct
mg_connection
*
conn
,
const
char
*
path
)
{
const
char
*
path
)
{
FILE
*
fp
;
FILE
*
fp
;
char
fname
[
PATH_MAX
];
char
fname
[
PATH_MAX
];
struct
vec
uri_vec
,
filename_vec
;
struct
vec
uri_vec
,
filename_vec
;
const
char
*
list
;
const
char
*
list
;
bool_
t
authorized
;
in
t
authorized
;
fp
=
NULL
;
fp
=
NULL
;
authorized
=
MG_TRUE
;
authorized
=
1
;
list
=
conn
->
ctx
->
config
->
protect
;
list
=
conn
->
ctx
->
config
[
URI_PROTECTION
]
;
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
while
((
list
=
next_option
(
list
,
&
uri_vec
,
&
filename_vec
))
!=
NULL
)
{
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
if
(
!
memcmp
(
conn
->
request_info
.
uri
,
uri_vec
.
ptr
,
uri_vec
.
len
))
{
(
void
)
mg_snprintf
(
conn
,
fname
,
sizeof
(
fname
),
"%.*s"
,
(
void
)
mg_snprintf
(
conn
,
fname
,
sizeof
(
fname
),
"%.*s"
,
...
@@ -2087,16 +2108,16 @@ static void send_authorization_request(struct mg_connection *conn) {
...
@@ -2087,16 +2108,16 @@ static void send_authorization_request(struct mg_connection *conn) {
"HTTP/1.1 401 Unauthorized
\r\n
"
"HTTP/1.1 401 Unauthorized
\r\n
"
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"WWW-Authenticate: Digest qop=
\"
auth
\"
, "
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
"realm=
\"
%s
\"
, nonce=
\"
%lu
\"\r\n\r\n
"
,
conn
->
ctx
->
config
->
auth_domain
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
]
,
(
unsigned
long
)
time
(
NULL
));
(
unsigned
long
)
time
(
NULL
));
}
}
static
bool_
t
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
static
in
t
is_authorized_for_put
(
struct
mg_connection
*
conn
)
{
FILE
*
fp
;
FILE
*
fp
;
int
ret
=
MG_FALSE
;
int
ret
=
0
;
fp
=
conn
->
ctx
->
config
->
put_delete_passwords_file
==
NULL
?
NULL
:
fp
=
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
]
==
NULL
?
NULL
:
mg_fopen
(
conn
->
ctx
->
config
->
put_delete_passwords_file
,
"r"
);
mg_fopen
(
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
]
,
"r"
);
if
(
fp
!=
NULL
)
{
if
(
fp
!=
NULL
)
{
...
@@ -2107,9 +2128,8 @@ static bool_t is_authorized_for_put(struct mg_connection *conn) {
...
@@ -2107,9 +2128,8 @@ static bool_t is_authorized_for_put(struct mg_connection *conn) {
return
ret
;
return
ret
;
}
}
enum
mg_error_t
mg_modify_passwords_file
(
struct
mg_context
*
ctx
,
int
mg_modify_passwords_file
(
struct
mg_context
*
ctx
,
const
char
*
fname
,
const
char
*
fname
,
const
char
*
user
,
const
char
*
user
,
const
char
*
pass
)
{
const
char
*
pass
)
{
int
found
;
int
found
;
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
char
line
[
512
],
u
[
512
],
d
[
512
],
ha1
[
33
],
tmp
[
PATH_MAX
];
const
char
*
domain
;
const
char
*
domain
;
...
@@ -2117,7 +2137,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
...
@@ -2117,7 +2137,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
found
=
0
;
found
=
0
;
fp
=
fp2
=
NULL
;
fp
=
fp2
=
NULL
;
domain
=
ctx
->
config
->
auth_domain
;
domain
=
ctx
->
config
[
AUTHENTICATION_DOMAIN
]
;
// Regard empty password as no password - remove user record.
// Regard empty password as no password - remove user record.
if
(
pass
[
0
]
==
'\0'
)
{
if
(
pass
[
0
]
==
'\0'
)
{
...
@@ -2134,10 +2154,10 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
...
@@ -2134,10 +2154,10 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
// Open the given file and temporary file
// Open the given file and temporary file
if
((
fp
=
mg_fopen
(
fname
,
"r"
))
==
NULL
)
{
if
((
fp
=
mg_fopen
(
fname
,
"r"
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"Cannot open %s: %s"
,
fname
,
strerror
(
errno
));
cry
(
fc
(
ctx
),
"Cannot open %s: %s"
,
fname
,
strerror
(
errno
));
return
MG_ERROR
;
return
0
;
}
else
if
((
fp2
=
mg_fopen
(
tmp
,
"w+"
))
==
NULL
)
{
}
else
if
((
fp2
=
mg_fopen
(
tmp
,
"w+"
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"Cannot open %s: %s"
,
tmp
,
strerror
(
errno
));
cry
(
fc
(
ctx
),
"Cannot open %s: %s"
,
tmp
,
strerror
(
errno
));
return
MG_ERROR
;
return
0
;
}
}
// Copy the stuff to temporary file
// Copy the stuff to temporary file
...
@@ -2171,7 +2191,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
...
@@ -2171,7 +2191,7 @@ enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
(
void
)
mg_remove
(
fname
);
(
void
)
mg_remove
(
fname
);
(
void
)
mg_rename
(
tmp
,
fname
);
(
void
)
mg_rename
(
tmp
,
fname
);
return
MG_SUCCESS
;
return
1
;
}
}
struct
de
{
struct
de
{
...
@@ -2452,7 +2472,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) {
...
@@ -2452,7 +2472,7 @@ static void parse_http_headers(char **buf, struct mg_request_info *ri) {
}
}
}
}
static
bool_
t
is_valid_http_method
(
const
char
*
method
)
{
static
in
t
is_valid_http_method
(
const
char
*
method
)
{
return
!
strcmp
(
method
,
"GET"
)
||
return
!
strcmp
(
method
,
"GET"
)
||
!
strcmp
(
method
,
"POST"
)
||
!
strcmp
(
method
,
"POST"
)
||
!
strcmp
(
method
,
"HEAD"
)
||
!
strcmp
(
method
,
"HEAD"
)
||
...
@@ -2461,8 +2481,8 @@ static bool_t is_valid_http_method(const char *method) {
...
@@ -2461,8 +2481,8 @@ static bool_t is_valid_http_method(const char *method) {
}
}
// Parse HTTP request, fill in mg_request_info structure.
// Parse HTTP request, fill in mg_request_info structure.
static
bool_
t
parse_http_request
(
char
*
buf
,
struct
mg_request_info
*
ri
)
{
static
in
t
parse_http_request
(
char
*
buf
,
struct
mg_request_info
*
ri
)
{
int
status
=
MG_FALSE
;
int
status
=
0
;
ri
->
request_method
=
skip
(
&
buf
,
" "
);
ri
->
request_method
=
skip
(
&
buf
,
" "
);
ri
->
uri
=
skip
(
&
buf
,
" "
);
ri
->
uri
=
skip
(
&
buf
,
" "
);
...
@@ -2473,7 +2493,7 @@ static bool_t parse_http_request(char *buf, struct mg_request_info *ri) {
...
@@ -2473,7 +2493,7 @@ static bool_t parse_http_request(char *buf, struct mg_request_info *ri) {
strncmp
(
ri
->
http_version
,
"HTTP/"
,
5
)
==
0
)
{
strncmp
(
ri
->
http_version
,
"HTTP/"
,
5
)
==
0
)
{
ri
->
http_version
+=
5
;
/* Skip "HTTP/" */
ri
->
http_version
+=
5
;
/* Skip "HTTP/" */
parse_http_headers
(
&
buf
,
ri
);
parse_http_headers
(
&
buf
,
ri
);
status
=
MG_TRUE
;
status
=
1
;
}
}
return
status
;
return
status
;
...
@@ -2505,15 +2525,13 @@ static int read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz,
...
@@ -2505,15 +2525,13 @@ static int read_request(FILE *fp, SOCKET sock, SSL *ssl, char *buf, int bufsiz,
// For given directory path, substitute it to valid index file.
// For given directory path, substitute it to valid index file.
// Return 0 if index file has been found, -1 if not found.
// Return 0 if index file has been found, -1 if not found.
// If the file is found, it's stats is returned in stp.
// If the file is found, it's stats is returned in stp.
static
bool_
t
substitute_index_file
(
struct
mg_connection
*
conn
,
char
*
path
,
static
in
t
substitute_index_file
(
struct
mg_connection
*
conn
,
char
*
path
,
size_t
path_len
,
struct
mgstat
*
stp
)
{
size_t
path_len
,
struct
mgstat
*
stp
)
{
const
char
*
list
;
const
char
*
list
=
conn
->
ctx
->
config
[
INDEX_FILES
]
;
struct
mgstat
st
;
struct
mgstat
st
;
struct
vec
filename_vec
;
struct
vec
filename_vec
;
size_t
n
;
size_t
n
=
strlen
(
path
);
bool_t
found
;
int
found
=
0
;
n
=
strlen
(
path
);
// The 'path' given to us points to the directory. Remove all trailing
// The 'path' given to us points to the directory. Remove all trailing
// directory separator characters from the end of the path, and
// directory separator characters from the end of the path, and
...
@@ -2525,9 +2543,6 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
...
@@ -2525,9 +2543,6 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
// Traverse index files list. For each entry, append it to the given
// Traverse index files list. For each entry, append it to the given
// path and see if the file exists. If it exists, break the loop
// path and see if the file exists. If it exists, break the loop
list
=
conn
->
ctx
->
config
->
index_files
;
found
=
MG_FALSE
;
while
((
list
=
next_option
(
list
,
&
filename_vec
,
NULL
))
!=
NULL
)
{
while
((
list
=
next_option
(
list
,
&
filename_vec
,
NULL
))
!=
NULL
)
{
// Ignore too long entries that may overflow path buffer
// Ignore too long entries that may overflow path buffer
...
@@ -2541,13 +2556,13 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
...
@@ -2541,13 +2556,13 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
if
(
mg_stat
(
path
,
&
st
)
==
0
)
{
if
(
mg_stat
(
path
,
&
st
)
==
0
)
{
// Yes it does, break the loop
// Yes it does, break the loop
*
stp
=
st
;
*
stp
=
st
;
found
=
MG_TRUE
;
found
=
1
;
break
;
break
;
}
}
}
}
// If no index file exists, restore directory path
// If no index file exists, restore directory path
if
(
found
==
MG_FALSE
)
{
if
(
!
found
)
{
path
[
n
]
=
'\0'
;
path
[
n
]
=
'\0'
;
}
}
...
@@ -2555,17 +2570,17 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
...
@@ -2555,17 +2570,17 @@ static bool_t substitute_index_file(struct mg_connection *conn, char *path,
}
}
// Return True if we should reply 304 Not Modified.
// Return True if we should reply 304 Not Modified.
static
bool_
t
is_not_modified
(
const
struct
mg_connection
*
conn
,
static
in
t
is_not_modified
(
const
struct
mg_connection
*
conn
,
const
struct
mgstat
*
stp
)
{
const
struct
mgstat
*
stp
)
{
const
char
*
ims
=
mg_get_header
(
conn
,
"If-Modified-Since"
);
const
char
*
ims
=
mg_get_header
(
conn
,
"If-Modified-Since"
);
return
ims
!=
NULL
&&
stp
->
mtime
<=
parse_date_string
(
ims
);
return
ims
!=
NULL
&&
stp
->
mtime
<=
parse_date_string
(
ims
);
}
}
static
bool_
t
handle_request_body
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
static
in
t
handle_request_body
(
struct
mg_connection
*
conn
,
FILE
*
fp
)
{
const
char
*
expect
,
*
data
;
const
char
*
expect
,
*
data
;
char
buf
[
BUFSIZ
];
char
buf
[
BUFSIZ
];
int
to_read
,
nread
,
data_len
;
int
to_read
,
nread
,
data_len
;
bool_t
status
=
MG_FALSE
;
int
success
=
0
;
expect
=
mg_get_header
(
conn
,
"Expect"
);
expect
=
mg_get_header
(
conn
,
"Expect"
);
assert
(
fp
!=
NULL
);
assert
(
fp
!=
NULL
);
...
@@ -2583,8 +2598,8 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) {
...
@@ -2583,8 +2598,8 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) {
assert
(
data_len
>=
0
);
assert
(
data_len
>=
0
);
if
(
conn
->
content_len
<=
(
int64_t
)
data_len
)
{
if
(
conn
->
content_len
<=
(
int64_t
)
data_len
)
{
s
tatus
=
fp
==
NULL
||
(
push
(
fp
,
INVALID_SOCKET
,
NULL
,
data
,
s
uccess
=
push
(
fp
,
INVALID_SOCKET
,
NULL
,
data
,
conn
->
content_len
)
==
conn
->
content_len
)
?
MG_TRUE
:
MG_FALSE
;
conn
->
content_len
)
==
conn
->
content_len
;
}
else
{
}
else
{
push
(
fp
,
INVALID_SOCKET
,
NULL
,
data
,
(
int64_t
)
data_len
);
push
(
fp
,
INVALID_SOCKET
,
NULL
,
data
,
(
int64_t
)
data_len
);
conn
->
consumed_content
+=
data_len
;
conn
->
consumed_content
+=
data_len
;
...
@@ -2601,18 +2616,17 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) {
...
@@ -2601,18 +2616,17 @@ static bool_t handle_request_body(struct mg_connection *conn, FILE *fp) {
conn
->
consumed_content
+=
nread
;
conn
->
consumed_content
+=
nread
;
}
}
if
(
conn
->
consumed_content
==
conn
->
content_len
)
{
if
(
conn
->
consumed_content
==
conn
->
content_len
)
{
s
tatus
=
MG_TRUE
;
s
uccess
=
1
;
}
}
}
}
// Each error code path in this function must send an error
// Each error code path in this function must send an error
if
(
status
!=
MG_TRUE
)
{
if
(
!
success
)
{
send_http_error
(
conn
,
577
,
http_500_error
,
send_http_error
(
conn
,
577
,
http_500_error
,
""
);
"%s"
,
"Error handling body data"
);
}
}
}
}
return
s
tatu
s
;
return
s
ucces
s
;
}
}
#if !defined(NO_CGI)
#if !defined(NO_CGI)
...
@@ -2681,7 +2695,7 @@ static void prepare_cgi_environment(struct mg_connection *conn,
...
@@ -2681,7 +2695,7 @@ static void prepare_cgi_environment(struct mg_connection *conn,
get_document_root
(
conn
,
&
root
);
get_document_root
(
conn
,
&
root
);
addenv
(
blk
,
"SERVER_NAME=%s"
,
conn
->
ctx
->
config
->
auth_domain
);
addenv
(
blk
,
"SERVER_NAME=%s"
,
conn
->
ctx
->
config
[
AUTHENTICATION_DOMAIN
]
);
addenv
(
blk
,
"SERVER_ROOT=%.*s"
,
root
.
len
,
root
.
ptr
);
addenv
(
blk
,
"SERVER_ROOT=%.*s"
,
root
.
len
,
root
.
ptr
);
addenv
(
blk
,
"DOCUMENT_ROOT=%.*s"
,
root
.
len
,
root
.
ptr
);
addenv
(
blk
,
"DOCUMENT_ROOT=%.*s"
,
root
.
len
,
root
.
ptr
);
...
@@ -2750,7 +2764,7 @@ static void prepare_cgi_environment(struct mg_connection *conn,
...
@@ -2750,7 +2764,7 @@ static void prepare_cgi_environment(struct mg_connection *conn,
}
}
// Add user-specified variables
// Add user-specified variables
s
=
conn
->
ctx
->
config
->
cgi_environment
;
s
=
conn
->
ctx
->
config
[
CGI_ENVIRONMENT
]
;
while
((
s
=
next_option
(
s
,
&
var_vec
,
NULL
))
!=
NULL
)
{
while
((
s
=
next_option
(
s
,
&
var_vec
,
NULL
))
!=
NULL
)
{
addenv
(
blk
,
"%.*s"
,
var_vec
.
len
,
var_vec
.
ptr
);
addenv
(
blk
,
"%.*s"
,
var_vec
.
len
,
var_vec
.
ptr
);
}
}
...
@@ -2942,7 +2956,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
...
@@ -2942,7 +2956,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
char
*
tag
,
int
include_level
)
{
char
*
tag
,
int
include_level
)
{
char
file_name
[
BUFSIZ
],
path
[
PATH_MAX
],
*
p
;
char
file_name
[
BUFSIZ
],
path
[
PATH_MAX
],
*
p
;
struct
vec
root
;
struct
vec
root
;
bool_
t
is_ssi
;
in
t
is_ssi
;
FILE
*
fp
;
FILE
*
fp
;
get_document_root
(
conn
,
&
root
);
get_document_root
(
conn
,
&
root
);
...
@@ -2975,7 +2989,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
...
@@ -2975,7 +2989,7 @@ static void do_ssi_include(struct mg_connection *conn, const char *ssi,
tag
,
path
,
strerror
(
ERRNO
));
tag
,
path
,
strerror
(
ERRNO
));
}
else
{
}
else
{
set_close_on_exec
(
fileno
(
fp
));
set_close_on_exec
(
fileno
(
fp
));
is_ssi
=
match_extension
(
path
,
conn
->
ctx
->
config
->
ssi_extensions
);
is_ssi
=
match_extension
(
path
,
conn
->
ctx
->
config
[
SSI_EXTENSIONS
]
);
if
(
is_ssi
)
{
if
(
is_ssi
)
{
send_ssi_file
(
conn
,
path
,
fp
,
include_level
+
1
);
send_ssi_file
(
conn
,
path
,
fp
,
include_level
+
1
);
}
else
{
}
else
{
...
@@ -3009,12 +3023,12 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
...
@@ -3009,12 +3023,12 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
return
;
return
;
}
}
in_ssi_tag
=
MG_FALSE
;
in_ssi_tag
=
0
;
len
=
0
;
len
=
0
;
while
((
ch
=
fgetc
(
fp
))
!=
EOF
)
{
while
((
ch
=
fgetc
(
fp
))
!=
EOF
)
{
if
(
in_ssi_tag
&&
ch
==
'>'
)
{
if
(
in_ssi_tag
&&
ch
==
'>'
)
{
in_ssi_tag
=
MG_FALSE
;
in_ssi_tag
=
0
;
buf
[
len
++
]
=
(
char
)
ch
;
buf
[
len
++
]
=
(
char
)
ch
;
buf
[
len
]
=
'\0'
;
buf
[
len
]
=
'\0'
;
assert
(
len
<=
(
int
)
sizeof
(
buf
));
assert
(
len
<=
(
int
)
sizeof
(
buf
));
...
@@ -3034,14 +3048,14 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
...
@@ -3034,14 +3048,14 @@ static void send_ssi_file(struct mg_connection *conn, const char *path,
}
else
if
(
in_ssi_tag
)
{
}
else
if
(
in_ssi_tag
)
{
if
(
len
==
5
&&
memcmp
(
buf
,
"<!--#"
,
5
)
!=
0
)
{
if
(
len
==
5
&&
memcmp
(
buf
,
"<!--#"
,
5
)
!=
0
)
{
// Not an SSI tag
// Not an SSI tag
in_ssi_tag
=
MG_FALSE
;
in_ssi_tag
=
0
;
}
else
if
(
len
==
(
int
)
sizeof
(
buf
)
-
2
)
{
}
else
if
(
len
==
(
int
)
sizeof
(
buf
)
-
2
)
{
cry
(
conn
,
"%s: SSI tag is too large"
,
path
);
cry
(
conn
,
"%s: SSI tag is too large"
,
path
);
len
=
0
;
len
=
0
;
}
}
buf
[
len
++
]
=
ch
&
0xff
;
buf
[
len
++
]
=
ch
&
0xff
;
}
else
if
(
ch
==
'<'
)
{
}
else
if
(
ch
==
'<'
)
{
in_ssi_tag
=
MG_TRUE
;
in_ssi_tag
=
1
;
if
(
len
>
0
)
{
if
(
len
>
0
)
{
(
void
)
mg_write
(
conn
,
buf
,
len
);
(
void
)
mg_write
(
conn
,
buf
,
len
);
}
}
...
@@ -3083,35 +3097,32 @@ static void handle_ssi_file_request(struct mg_connection *conn,
...
@@ -3083,35 +3097,32 @@ static void handle_ssi_file_request(struct mg_connection *conn,
// and Mongoose must decide what action to take: serve a file, or
// and Mongoose must decide what action to take: serve a file, or
// a directory, or call embedded function, etcetera.
// a directory, or call embedded function, etcetera.
static
void
handle_request
(
struct
mg_connection
*
conn
)
{
static
void
handle_request
(
struct
mg_connection
*
conn
)
{
struct
mg_request_info
*
ri
;
struct
mg_request_info
*
ri
=
&
conn
->
request_info
;
char
path
[
PATH_MAX
];
char
path
[
PATH_MAX
];
int
uri_len
;
int
uri_len
;
struct
mgstat
st
;
struct
mgstat
st
;
mg_callback_t
new_request_callback
;
ri
=
&
conn
->
request_info
;
if
((
conn
->
request_info
.
query_string
=
strchr
(
ri
->
uri
,
'?'
))
!=
NULL
)
{
if
((
conn
->
request_info
.
query_string
=
strchr
(
ri
->
uri
,
'?'
))
!=
NULL
)
{
*
conn
->
request_info
.
query_string
++
=
'\0'
;
*
conn
->
request_info
.
query_string
++
=
'\0'
;
}
}
uri_len
=
strlen
(
ri
->
uri
);
uri_len
=
strlen
(
ri
->
uri
);
new_request_callback
=
conn
->
ctx
->
config
->
new_request_handler
;
(
void
)
url_decode
(
ri
->
uri
,
uri_len
,
ri
->
uri
,
uri_len
+
1
,
0
);
(
void
)
url_decode
(
ri
->
uri
,
uri_len
,
ri
->
uri
,
uri_len
+
1
,
MG_FALSE
);
remove_double_dots_and_double_slashes
(
ri
->
uri
);
remove_double_dots_and_double_slashes
(
ri
->
uri
);
convert_uri_to_file_name
(
conn
,
ri
->
uri
,
path
,
sizeof
(
path
));
convert_uri_to_file_name
(
conn
,
ri
->
uri
,
path
,
sizeof
(
path
));
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
DEBUG_TRACE
((
"%s"
,
ri
->
uri
));
if
(
new_request_callback
&&
new_request_callback
(
conn
,
ri
)
==
MG_TRUE
)
{
if
(
call_user
(
conn
,
MG_NEW_REQUEST
)
!=
NULL
)
{
// Do nothing, callback has served the request
// Do nothing, callback has served the request
}
else
if
(
!
check_authorization
(
conn
,
path
))
{
}
else
if
(
!
check_authorization
(
conn
,
path
))
{
send_authorization_request
(
conn
);
send_authorization_request
(
conn
);
}
else
if
(
strstr
(
path
,
PASSWORDS_FILE_NAME
))
{
}
else
if
(
strstr
(
path
,
PASSWORDS_FILE_NAME
))
{
// Do not allow to view passwords files
// Do not allow to view passwords files
send_http_error
(
conn
,
403
,
"Forbidden"
,
"Access Forbidden"
);
send_http_error
(
conn
,
403
,
"Forbidden"
,
"Access Forbidden"
);
}
else
if
(
conn
->
ctx
->
config
->
document_root
==
NULL
)
{
}
else
if
(
conn
->
ctx
->
config
[
DOCUMENT_ROOT
]
==
NULL
)
{
send_http_error
(
conn
,
404
,
"Not Found"
,
"Not Found"
);
send_http_error
(
conn
,
404
,
"Not Found"
,
"Not Found"
);
}
else
if
((
!
strcmp
(
ri
->
request_method
,
"PUT"
)
||
}
else
if
((
!
strcmp
(
ri
->
request_method
,
"PUT"
)
||
!
strcmp
(
ri
->
request_method
,
"DELETE"
))
&&
!
strcmp
(
ri
->
request_method
,
"DELETE"
))
&&
(
conn
->
ctx
->
config
->
put_delete_passwords_file
==
NULL
||
(
conn
->
ctx
->
config
[
PUT_DELETE_PASSWORDS_FILE
]
==
NULL
||
!
is_authorized_for_put
(
conn
)))
{
!
is_authorized_for_put
(
conn
)))
{
send_authorization_request
(
conn
);
send_authorization_request
(
conn
);
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"PUT"
))
{
}
else
if
(
!
strcmp
(
ri
->
request_method
,
"PUT"
))
{
...
@@ -3130,14 +3141,14 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -3130,14 +3141,14 @@ static void handle_request(struct mg_connection *conn) {
"HTTP/1.1 301 Moved Permanently
\r\n
"
"HTTP/1.1 301 Moved Permanently
\r\n
"
"Location: %s/
\r\n\r\n
"
,
ri
->
uri
);
"Location: %s/
\r\n\r\n
"
,
ri
->
uri
);
}
else
if
(
st
.
is_directory
&&
}
else
if
(
st
.
is_directory
&&
substitute_index_file
(
conn
,
path
,
sizeof
(
path
),
&
st
)
==
MG_FALSE
)
{
!
substitute_index_file
(
conn
,
path
,
sizeof
(
path
),
&
st
)
)
{
if
(
conn
->
ctx
->
config
->
enable_directory_listing
)
{
if
(
!
mg_strcasecmp
(
conn
->
ctx
->
config
[
ENABLE_DIRECTORY_LISTING
],
"yes"
)
)
{
handle_directory_request
(
conn
,
path
);
handle_directory_request
(
conn
,
path
);
}
else
{
}
else
{
send_http_error
(
conn
,
403
,
"Directory Listing Denied"
,
send_http_error
(
conn
,
403
,
"Directory Listing Denied"
,
"Directory listing denied"
);
"Directory listing denied"
);
}
}
}
else
if
(
match_extension
(
path
,
conn
->
ctx
->
config
->
cgi_extensions
))
{
}
else
if
(
match_extension
(
path
,
conn
->
ctx
->
config
[
CGI_EXTENSIONS
]
))
{
if
(
strcmp
(
ri
->
request_method
,
"POST"
)
&&
if
(
strcmp
(
ri
->
request_method
,
"POST"
)
&&
strcmp
(
ri
->
request_method
,
"GET"
))
{
strcmp
(
ri
->
request_method
,
"GET"
))
{
send_http_error
(
conn
,
501
,
"Not Implemented"
,
send_http_error
(
conn
,
501
,
"Not Implemented"
,
...
@@ -3145,7 +3156,7 @@ static void handle_request(struct mg_connection *conn) {
...
@@ -3145,7 +3156,7 @@ static void handle_request(struct mg_connection *conn) {
}
else
{
}
else
{
handle_cgi_request
(
conn
,
path
);
handle_cgi_request
(
conn
,
path
);
}
}
}
else
if
(
match_extension
(
path
,
conn
->
ctx
->
config
->
ssi_extensions
))
{
}
else
if
(
match_extension
(
path
,
conn
->
ctx
->
config
[
SSI_EXTENSIONS
]
))
{
handle_ssi_file_request
(
conn
,
path
);
handle_ssi_file_request
(
conn
,
path
);
}
else
if
(
is_not_modified
(
conn
,
&
st
))
{
}
else
if
(
is_not_modified
(
conn
,
&
st
))
{
send_http_error
(
conn
,
304
,
"Not Modified"
,
""
);
send_http_error
(
conn
,
304
,
"Not Modified"
,
""
);
...
@@ -3163,28 +3174,28 @@ static void close_all_listening_sockets(struct mg_context *ctx) {
...
@@ -3163,28 +3174,28 @@ static void close_all_listening_sockets(struct mg_context *ctx) {
}
}
}
}
static
enum
mg_error_
t
set_ports_option
(
struct
mg_context
*
ctx
)
{
static
in
t
set_ports_option
(
struct
mg_context
*
ctx
)
{
SOCKET
sock
;
SOCKET
sock
;
int
is_ssl
;
const
char
*
list
=
ctx
->
config
[
LISTENING_PORTS
];
int
is_ssl
,
success
=
1
;
struct
vec
vec
;
struct
vec
vec
;
struct
socket
*
listener
;
struct
socket
*
listener
;
const
char
*
list
=
ctx
->
config
->
listening_ports
;
while
((
list
=
next_option
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
while
(
success
&&
(
list
=
next_option
(
list
,
&
vec
,
NULL
))
!=
NULL
)
{
is_ssl
=
vec
.
ptr
[
vec
.
len
-
1
]
==
's'
?
MG_TRUE
:
MG_FALSE
;
is_ssl
=
vec
.
ptr
[
vec
.
len
-
1
]
==
's'
;
if
((
listener
=
calloc
(
1
,
sizeof
(
*
listener
)))
==
NULL
)
{
if
((
listener
=
calloc
(
1
,
sizeof
(
*
listener
)))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s"
,
"Too many listeninig sockets"
);
cry
(
fc
(
ctx
),
"%s"
,
"Too many listeninig sockets"
);
return
MG_ERROR
;
success
=
0
;
}
else
if
((
sock
=
mg_open_listening_port
(
ctx
,
}
else
if
((
sock
=
mg_open_listening_port
(
ctx
,
vec
.
ptr
,
&
listener
->
lsa
))
==
INVALID_SOCKET
)
{
vec
.
ptr
,
&
listener
->
lsa
))
==
INVALID_SOCKET
)
{
cry
(
fc
(
ctx
),
"cannot bind to %.*s"
,
vec
.
len
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"cannot bind to %.*s"
,
vec
.
len
,
vec
.
ptr
);
return
MG_ERROR
;
success
=
0
;
}
else
if
(
is_ssl
==
MG_TRUE
&&
ctx
->
ssl_ctx
==
NULL
)
{
}
else
if
(
is_ssl
&&
ctx
->
ssl_ctx
==
NULL
)
{
(
void
)
closesocket
(
sock
);
(
void
)
closesocket
(
sock
);
cry
(
fc
(
ctx
),
"cannot add SSL socket, please specify "
cry
(
fc
(
ctx
),
"cannot add SSL socket, please specify "
"-ssl_cert option BEFORE -ports option"
);
"-ssl_cert option BEFORE -ports option"
);
return
MG_ERROR
;
success
=
0
;
}
else
{
}
else
{
listener
->
sock
=
sock
;
listener
->
sock
=
sock
;
listener
->
is_ssl
=
is_ssl
;
listener
->
is_ssl
=
is_ssl
;
...
@@ -3193,7 +3204,11 @@ static enum mg_error_t set_ports_option(struct mg_context *ctx) {
...
@@ -3193,7 +3204,11 @@ static enum mg_error_t set_ports_option(struct mg_context *ctx) {
}
}
}
}
return
MG_SUCCESS
;
if
(
!
success
)
{
close_all_listening_sockets
(
ctx
);
}
return
success
;
}
}
static
void
log_header
(
const
struct
mg_connection
*
conn
,
const
char
*
header
,
static
void
log_header
(
const
struct
mg_connection
*
conn
,
const
char
*
header
,
...
@@ -3212,8 +3227,8 @@ static void log_access(const struct mg_connection *conn) {
...
@@ -3212,8 +3227,8 @@ static void log_access(const struct mg_connection *conn) {
FILE
*
fp
;
FILE
*
fp
;
char
date
[
64
];
char
date
[
64
];
fp
=
conn
->
ctx
->
config
->
access_log_file
==
NULL
?
NULL
:
fp
=
conn
->
ctx
->
config
[
ACCESS_LOG_FILE
]
==
NULL
?
NULL
:
mg_fopen
(
conn
->
ctx
->
config
->
access_log_file
,
"a+"
);
mg_fopen
(
conn
->
ctx
->
config
[
ACCESS_LOG_FILE
]
,
"a+"
);
if
(
fp
==
NULL
)
if
(
fp
==
NULL
)
return
;
return
;
...
@@ -3243,22 +3258,21 @@ static void log_access(const struct mg_connection *conn) {
...
@@ -3243,22 +3258,21 @@ static void log_access(const struct mg_connection *conn) {
(
void
)
fclose
(
fp
);
(
void
)
fclose
(
fp
);
}
}
static
bool_
t
isbyte
(
int
n
)
{
static
in
t
isbyte
(
int
n
)
{
return
n
>=
0
&&
n
<=
255
?
MG_TRUE
:
MG_FALSE
;
return
n
>=
0
&&
n
<=
255
;
}
}
// Verify given socket address against the ACL.
// Verify given socket address against the ACL.
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
static
enum
mg_error_t
check_acl
(
struct
mg_context
*
ctx
,
static
int
check_acl
(
struct
mg_context
*
ctx
,
const
struct
usa
*
usa
)
{
const
struct
usa
*
usa
)
{
int
a
,
b
,
c
,
d
,
n
,
mask
,
allowed
;
int
a
,
b
,
c
,
d
,
n
,
mask
,
allowed
;
char
flag
;
char
flag
;
uint32_t
acl_subnet
,
acl_mask
,
remote_ip
;
uint32_t
acl_subnet
,
acl_mask
,
remote_ip
;
struct
vec
vec
;
struct
vec
vec
;
const
char
*
list
=
ctx
->
config
->
acl
;
const
char
*
list
=
ctx
->
config
[
ACCESS_CONTROL_LIST
]
;
if
(
list
==
NULL
)
{
if
(
list
==
NULL
)
{
return
MG_SUCCESS
;
return
1
;
}
}
(
void
)
memcpy
(
&
remote_ip
,
&
usa
->
u
.
sin
.
sin_addr
,
sizeof
(
remote_ip
));
(
void
)
memcpy
(
&
remote_ip
,
&
usa
->
u
.
sin
.
sin_addr
,
sizeof
(
remote_ip
));
...
@@ -3271,18 +3285,18 @@ static enum mg_error_t check_acl(struct mg_context *ctx,
...
@@ -3271,18 +3285,18 @@ static enum mg_error_t check_acl(struct mg_context *ctx,
if
(
sscanf
(
vec
.
ptr
,
"%c%d.%d.%d.%d%n"
,
&
flag
,
&
a
,
&
b
,
&
c
,
&
d
,
&
n
)
!=
5
)
{
if
(
sscanf
(
vec
.
ptr
,
"%c%d.%d.%d.%d%n"
,
&
flag
,
&
a
,
&
b
,
&
c
,
&
d
,
&
n
)
!=
5
)
{
cry
(
fc
(
ctx
),
"%s: subnet must be [+|-]x.x.x.x[/x]"
,
__func__
);
cry
(
fc
(
ctx
),
"%s: subnet must be [+|-]x.x.x.x[/x]"
,
__func__
);
return
MG_ERROR
;
return
0
;
}
else
if
(
flag
!=
'+'
&&
flag
!=
'-'
)
{
}
else
if
(
flag
!=
'+'
&&
flag
!=
'-'
)
{
cry
(
fc
(
ctx
),
"%s: flag must be + or -: [%s]"
,
__func__
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: flag must be + or -: [%s]"
,
__func__
,
vec
.
ptr
);
return
MG_ERROR
;
return
0
;
}
else
if
(
!
isbyte
(
a
)
||!
isbyte
(
b
)
||!
isbyte
(
c
)
||!
isbyte
(
d
))
{
}
else
if
(
!
isbyte
(
a
)
||!
isbyte
(
b
)
||!
isbyte
(
c
)
||!
isbyte
(
d
))
{
cry
(
fc
(
ctx
),
"%s: bad ip address: [%s]"
,
__func__
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: bad ip address: [%s]"
,
__func__
,
vec
.
ptr
);
return
MG_ERROR
;
return
0
;
}
else
if
(
sscanf
(
vec
.
ptr
+
n
,
"/%d"
,
&
mask
)
==
0
)
{
}
else
if
(
sscanf
(
vec
.
ptr
+
n
,
"/%d"
,
&
mask
)
==
0
)
{
// Do nothing, no mask specified
// Do nothing, no mask specified
}
else
if
(
mask
<
0
||
mask
>
32
)
{
}
else
if
(
mask
<
0
||
mask
>
32
)
{
cry
(
fc
(
ctx
),
"%s: bad subnet mask: %d [%s]"
,
__func__
,
n
,
vec
.
ptr
);
cry
(
fc
(
ctx
),
"%s: bad subnet mask: %d [%s]"
,
__func__
,
n
,
vec
.
ptr
);
return
MG_ERROR
;
return
0
;
}
}
acl_subnet
=
(
a
<<
24
)
|
(
b
<<
16
)
|
(
c
<<
8
)
|
d
;
acl_subnet
=
(
a
<<
24
)
|
(
b
<<
16
)
|
(
c
<<
8
)
|
d
;
...
@@ -3293,7 +3307,7 @@ static enum mg_error_t check_acl(struct mg_context *ctx,
...
@@ -3293,7 +3307,7 @@ static enum mg_error_t check_acl(struct mg_context *ctx,
}
}
}
}
return
allowed
==
'+'
?
MG_SUCCESS
:
MG_ERROR
;
return
allowed
==
'+'
;
}
}
static
void
add_to_set
(
SOCKET
fd
,
fd_set
*
set
,
int
*
max_fd
)
{
static
void
add_to_set
(
SOCKET
fd
,
fd_set
*
set
,
int
*
max_fd
)
{
...
@@ -3304,13 +3318,13 @@ static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
...
@@ -3304,13 +3318,13 @@ static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
}
}
#if !defined(_WIN32)
#if !defined(_WIN32)
static
enum
mg_error_
t
set_uid_option
(
struct
mg_context
*
ctx
)
{
static
in
t
set_uid_option
(
struct
mg_context
*
ctx
)
{
struct
passwd
*
pw
;
struct
passwd
*
pw
;
const
char
*
uid
=
ctx
->
config
->
uid
;
const
char
*
uid
=
ctx
->
config
[
RUN_AS_USER
]
;
enum
mg_error_t
error
;
int
success
=
0
;
if
(
uid
==
NULL
)
{
if
(
uid
==
NULL
)
{
error
=
MG_SUCCESS
;
success
=
1
;
}
else
{
}
else
{
if
((
pw
=
getpwnam
(
uid
))
==
NULL
)
{
if
((
pw
=
getpwnam
(
uid
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: unknown user [%s]"
,
__func__
,
uid
);
cry
(
fc
(
ctx
),
"%s: unknown user [%s]"
,
__func__
,
uid
);
...
@@ -3319,11 +3333,11 @@ static enum mg_error_t set_uid_option(struct mg_context *ctx) {
...
@@ -3319,11 +3333,11 @@ static enum mg_error_t set_uid_option(struct mg_context *ctx) {
}
else
if
(
setuid
(
pw
->
pw_uid
)
==
-
1
)
{
}
else
if
(
setuid
(
pw
->
pw_uid
)
==
-
1
)
{
cry
(
fc
(
ctx
),
"%s: setuid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
cry
(
fc
(
ctx
),
"%s: setuid(%s): %s"
,
__func__
,
uid
,
strerror
(
errno
));
}
else
{
}
else
{
error
=
MG_SUCCESS
;
success
=
1
;
}
}
}
}
return
error
;
return
success
;
}
}
#endif // !_WIN32
#endif // !_WIN32
...
@@ -3346,15 +3360,15 @@ static unsigned long ssl_id_callback(void) {
...
@@ -3346,15 +3360,15 @@ static unsigned long ssl_id_callback(void) {
return
(
unsigned
long
)
pthread_self
();
return
(
unsigned
long
)
pthread_self
();
}
}
static
bool_
t
load_dll
(
struct
mg_context
*
ctx
,
const
char
*
dll_name
,
static
in
t
load_dll
(
struct
mg_context
*
ctx
,
const
char
*
dll_name
,
struct
ssl_func
*
sw
)
{
struct
ssl_func
*
sw
)
{
union
{
void
*
p
;
void
(
*
fp
)(
void
);}
u
;
union
{
void
*
p
;
void
(
*
fp
)(
void
);}
u
;
void
*
dll_handle
;
void
*
dll_handle
;
struct
ssl_func
*
fp
;
struct
ssl_func
*
fp
;
if
((
dll_handle
=
dlopen
(
dll_name
,
RTLD_LAZY
))
==
NULL
)
{
if
((
dll_handle
=
dlopen
(
dll_name
,
RTLD_LAZY
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: cannot load %s"
,
__func__
,
dll_name
);
cry
(
fc
(
ctx
),
"%s: cannot load %s"
,
__func__
,
dll_name
);
return
MG_FALSE
;
return
0
;
}
}
for
(
fp
=
sw
;
fp
->
name
!=
NULL
;
fp
++
)
{
for
(
fp
=
sw
;
fp
->
name
!=
NULL
;
fp
++
)
{
...
@@ -3368,28 +3382,28 @@ static bool_t load_dll(struct mg_context *ctx, const char *dll_name,
...
@@ -3368,28 +3382,28 @@ static bool_t load_dll(struct mg_context *ctx, const char *dll_name,
#endif
/* _WIN32 */
#endif
/* _WIN32 */
if
(
u
.
fp
==
NULL
)
{
if
(
u
.
fp
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: cannot find %s"
,
__func__
,
fp
->
name
);
cry
(
fc
(
ctx
),
"%s: cannot find %s"
,
__func__
,
fp
->
name
);
return
MG_FALSE
;
return
0
;
}
else
{
}
else
{
fp
->
ptr
=
u
.
fp
;
fp
->
ptr
=
u
.
fp
;
}
}
}
}
return
MG_TRUE
;
return
1
;
}
}
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static
enum
mg_error_
t
set_ssl_option
(
struct
mg_context
*
ctx
)
{
static
in
t
set_ssl_option
(
struct
mg_context
*
ctx
)
{
SSL_CTX
*
CTX
;
SSL_CTX
*
CTX
;
int
i
,
size
;
int
i
,
size
;
const
char
*
pem
=
ctx
->
config
->
ssl_certificate
;
const
char
*
pem
=
ctx
->
config
[
SSL_CERTIFICATE
]
;
if
(
pem
==
NULL
)
{
if
(
pem
==
NULL
)
{
return
MG_SUCCESS
;
return
1
;
}
}
if
(
load_dll
(
ctx
,
SSL_LIB
,
ssl_sw
)
==
MG_FALSE
||
if
(
!
load_dll
(
ctx
,
SSL_LIB
,
ssl_sw
)
||
load_dll
(
ctx
,
CRYPTO_LIB
,
crypto_sw
)
==
MG_FALSE
)
{
!
load_dll
(
ctx
,
CRYPTO_LIB
,
crypto_sw
)
)
{
return
MG_ERROR
;
return
0
;
}
}
// Initialize SSL crap
// Initialize SSL crap
...
@@ -3398,18 +3412,18 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
...
@@ -3398,18 +3412,18 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
if
((
CTX
=
SSL_CTX_new
(
SSLv23_server_method
()))
==
NULL
)
{
if
((
CTX
=
SSL_CTX_new
(
SSLv23_server_method
()))
==
NULL
)
{
cry
(
fc
(
ctx
),
"SSL_CTX_new error: %s"
,
ssl_error
());
cry
(
fc
(
ctx
),
"SSL_CTX_new error: %s"
,
ssl_error
());
}
else
if
(
ctx
->
config
->
ssl_password_handler
!=
NULL
)
{
}
else
if
(
ctx
->
user_callback
!=
NULL
)
{
SSL_CTX_set_default_passwd_cb
(
CTX
,
ctx
->
config
->
ssl_password_handler
);
ctx
->
user_callback
(
MG_INIT_SSL
,
(
struct
mg_connection
*
)
CTX
,
NULL
);
}
}
if
(
CTX
!=
NULL
&&
SSL_CTX_use_certificate_file
(
CTX
,
pem
,
if
(
CTX
!=
NULL
&&
SSL_CTX_use_certificate_file
(
CTX
,
pem
,
SSL_FILETYPE_PEM
)
==
0
)
{
SSL_FILETYPE_PEM
)
==
0
)
{
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
__func__
,
pem
,
ssl_error
());
return
MG_ERROR
;
return
0
;
}
else
if
(
CTX
!=
NULL
&&
SSL_CTX_use_PrivateKey_file
(
CTX
,
pem
,
}
else
if
(
CTX
!=
NULL
&&
SSL_CTX_use_PrivateKey_file
(
CTX
,
pem
,
SSL_FILETYPE_PEM
)
==
0
)
{
SSL_FILETYPE_PEM
)
==
0
)
{
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
NULL
,
pem
,
ssl_error
());
cry
(
fc
(
ctx
),
"%s: cannot open %s: %s"
,
NULL
,
pem
,
ssl_error
());
return
MG_ERROR
;
return
0
;
}
}
// Initialize locking callbacks, needed for thread safety.
// Initialize locking callbacks, needed for thread safety.
...
@@ -3417,7 +3431,7 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
...
@@ -3417,7 +3431,7 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
size
=
sizeof
(
pthread_mutex_t
)
*
CRYPTO_num_locks
();
size
=
sizeof
(
pthread_mutex_t
)
*
CRYPTO_num_locks
();
if
((
ssl_mutexes
=
(
pthread_mutex_t
*
)
malloc
(
size
))
==
NULL
)
{
if
((
ssl_mutexes
=
(
pthread_mutex_t
*
)
malloc
(
size
))
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: cannot allocate mutexes: %s"
,
__func__
,
ssl_error
());
cry
(
fc
(
ctx
),
"%s: cannot allocate mutexes: %s"
,
__func__
,
ssl_error
());
return
MG_ERROR
;
return
0
;
}
}
for
(
i
=
0
;
i
<
CRYPTO_num_locks
();
i
++
)
{
for
(
i
=
0
;
i
<
CRYPTO_num_locks
();
i
++
)
{
...
@@ -3430,24 +3444,25 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
...
@@ -3430,24 +3444,25 @@ static enum mg_error_t set_ssl_option(struct mg_context *ctx) {
// Done with everything. Save the context.
// Done with everything. Save the context.
ctx
->
ssl_ctx
=
CTX
;
ctx
->
ssl_ctx
=
CTX
;
return
MG_SUCCESS
;
return
1
;
}
}
#endif // !NO_SSL
#endif // !NO_SSL
static
enum
mg_error_
t
set_gpass_option
(
struct
mg_context
*
ctx
)
{
static
in
t
set_gpass_option
(
struct
mg_context
*
ctx
)
{
struct
mgstat
mgstat
;
struct
mgstat
mgstat
;
const
char
*
path
=
ctx
->
config
->
global_passwords_file
;
const
char
*
path
=
ctx
->
config
[
GLOBAL_PASSWORDS_FILE
]
;
return
path
==
NULL
||
mg_stat
(
path
,
&
mgstat
)
==
0
?
MG_SUCCESS
:
MG_ERROR
;
return
path
==
NULL
||
mg_stat
(
path
,
&
mgstat
)
==
0
;
}
}
static
enum
mg_error_
t
set_acl_option
(
struct
mg_context
*
ctx
)
{
static
in
t
set_acl_option
(
struct
mg_context
*
ctx
)
{
struct
usa
fake
;
struct
usa
fake
;
return
check_acl
(
ctx
,
&
fake
);
return
check_acl
(
ctx
,
&
fake
);
}
}
static
bool_t
verify_document_root
(
struct
mg_context
*
ctx
,
const
char
*
root
)
{
static
int
verify_document_root
(
struct
mg_context
*
ctx
)
{
char
path
[
PATH_MAX
],
*
p
;
char
path
[
PATH_MAX
],
*
p
;
struct
mgstat
buf
;
struct
mgstat
buf
;
const
char
*
root
=
ctx
->
config
[
DOCUMENT_ROOT
];
if
((
p
=
strchr
(
root
,
','
))
==
NULL
)
{
if
((
p
=
strchr
(
root
,
','
))
==
NULL
)
{
mg_strlcpy
(
path
,
root
,
sizeof
(
path
));
mg_strlcpy
(
path
,
root
,
sizeof
(
path
));
...
@@ -3457,9 +3472,9 @@ static bool_t verify_document_root(struct mg_context *ctx, const char *root) {
...
@@ -3457,9 +3472,9 @@ static bool_t verify_document_root(struct mg_context *ctx, const char *root) {
if
(
mg_stat
(
path
,
&
buf
)
!=
0
)
{
if
(
mg_stat
(
path
,
&
buf
)
!=
0
)
{
cry
(
fc
(
ctx
),
"Invalid root directory:
\"
%s
\"
"
,
root
);
cry
(
fc
(
ctx
),
"Invalid root directory:
\"
%s
\"
"
,
root
);
return
MG_FALSE
;
return
0
;
}
}
return
MG_TRUE
;
return
1
;
}
}
static
void
reset_per_request_attributes
(
struct
mg_connection
*
conn
)
{
static
void
reset_per_request_attributes
(
struct
mg_connection
*
conn
)
{
...
@@ -3562,7 +3577,7 @@ static void process_new_connection(struct mg_connection *conn) {
...
@@ -3562,7 +3577,7 @@ static void process_new_connection(struct mg_connection *conn) {
}
}
// Worker threads take accepted socket from the queue
// Worker threads take accepted socket from the queue
static
bool_
t
consume_socket
(
struct
mg_context
*
ctx
,
struct
socket
*
sp
)
{
static
in
t
consume_socket
(
struct
mg_context
*
ctx
,
struct
socket
*
sp
)
{
(
void
)
pthread_mutex_lock
(
&
ctx
->
mutex
);
(
void
)
pthread_mutex_lock
(
&
ctx
->
mutex
);
DEBUG_TRACE
((
"going idle"
));
DEBUG_TRACE
((
"going idle"
));
...
@@ -3574,7 +3589,7 @@ static bool_t consume_socket(struct mg_context *ctx, struct socket *sp) {
...
@@ -3574,7 +3589,7 @@ static bool_t consume_socket(struct mg_context *ctx, struct socket *sp) {
// If this happens, it is time to exit.
// If this happens, it is time to exit.
if
(
ctx
->
stop_flag
)
{
if
(
ctx
->
stop_flag
)
{
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
return
MG_FALSE
;
return
0
;
}
}
assert
(
ctx
->
sq_head
>
ctx
->
sq_tail
);
assert
(
ctx
->
sq_head
>
ctx
->
sq_tail
);
...
@@ -3592,7 +3607,7 @@ static bool_t consume_socket(struct mg_context *ctx, struct socket *sp) {
...
@@ -3592,7 +3607,7 @@ static bool_t consume_socket(struct mg_context *ctx, struct socket *sp) {
(
void
)
pthread_cond_signal
(
&
ctx
->
sq_empty
);
(
void
)
pthread_cond_signal
(
&
ctx
->
sq_empty
);
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
return
MG_TRUE
;
return
1
;
}
}
static
void
worker_thread
(
struct
mg_context
*
ctx
)
{
static
void
worker_thread
(
struct
mg_context
*
ctx
)
{
...
@@ -3661,14 +3676,14 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
...
@@ -3661,14 +3676,14 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
static
void
accept_new_connection
(
const
struct
socket
*
listener
,
static
void
accept_new_connection
(
const
struct
socket
*
listener
,
struct
mg_context
*
ctx
)
{
struct
mg_context
*
ctx
)
{
struct
socket
accepted
;
struct
socket
accepted
;
bool_
t
allowed
;
in
t
allowed
;
accepted
.
rsa
.
len
=
sizeof
(
accepted
.
rsa
.
u
.
sin
);
accepted
.
rsa
.
len
=
sizeof
(
accepted
.
rsa
.
u
.
sin
);
accepted
.
lsa
=
listener
->
lsa
;
accepted
.
lsa
=
listener
->
lsa
;
accepted
.
sock
=
accept
(
listener
->
sock
,
&
accepted
.
rsa
.
u
.
sa
,
&
accepted
.
rsa
.
len
);
accepted
.
sock
=
accept
(
listener
->
sock
,
&
accepted
.
rsa
.
u
.
sa
,
&
accepted
.
rsa
.
len
);
if
(
accepted
.
sock
!=
INVALID_SOCKET
)
{
if
(
accepted
.
sock
!=
INVALID_SOCKET
)
{
allowed
=
check_acl
(
ctx
,
&
accepted
.
rsa
)
==
MG_SUCCESS
;
allowed
=
check_acl
(
ctx
,
&
accepted
.
rsa
);
if
(
allowed
==
MG_SUCCESS
)
{
if
(
allowed
)
{
// Put accepted socket structure into the queue
// Put accepted socket structure into the queue
DEBUG_TRACE
((
"accepted socket %d"
,
accepted
.
sock
));
DEBUG_TRACE
((
"accepted socket %d"
,
accepted
.
sock
));
accepted
.
is_ssl
=
listener
->
is_ssl
;
accepted
.
is_ssl
=
listener
->
is_ssl
;
...
@@ -3729,11 +3744,6 @@ static void master_thread(struct mg_context *ctx) {
...
@@ -3729,11 +3744,6 @@ static void master_thread(struct mg_context *ctx) {
}
}
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
(
void
)
pthread_mutex_unlock
(
&
ctx
->
mutex
);
// Deallocate SSL context
if
(
ctx
->
ssl_ctx
!=
NULL
)
{
SSL_CTX_free
(
ctx
->
ssl_ctx
);
}
// All threads exited, no sync is needed. Destroy mutex and condvars
// All threads exited, no sync is needed. Destroy mutex and condvars
(
void
)
pthread_mutex_destroy
(
&
ctx
->
mutex
);
(
void
)
pthread_mutex_destroy
(
&
ctx
->
mutex
);
(
void
)
pthread_cond_destroy
(
&
ctx
->
cond
);
(
void
)
pthread_cond_destroy
(
&
ctx
->
cond
);
...
@@ -3746,6 +3756,24 @@ static void master_thread(struct mg_context *ctx) {
...
@@ -3746,6 +3756,24 @@ static void master_thread(struct mg_context *ctx) {
DEBUG_TRACE
((
"exiting"
));
DEBUG_TRACE
((
"exiting"
));
}
}
static
void
free_context
(
struct
mg_context
*
ctx
)
{
int
i
;
// Deallocate config parameters
for
(
i
=
0
;
i
<
NUM_OPTIONS
;
i
++
)
{
if
(
ctx
->
config
[
i
]
!=
NULL
)
free
(
ctx
->
config
[
i
]);
}
// Deallocate SSL context
if
(
ctx
->
ssl_ctx
!=
NULL
)
{
SSL_CTX_free
(
ctx
->
ssl_ctx
);
}
// Deallocate context itself
free
(
ctx
);
}
void
mg_stop
(
struct
mg_context
*
ctx
)
{
void
mg_stop
(
struct
mg_context
*
ctx
)
{
ctx
->
stop_flag
=
1
;
ctx
->
stop_flag
=
1
;
...
@@ -3753,15 +3781,16 @@ void mg_stop(struct mg_context *ctx) {
...
@@ -3753,15 +3781,16 @@ void mg_stop(struct mg_context *ctx) {
while
(
ctx
->
stop_flag
!=
2
)
{
while
(
ctx
->
stop_flag
!=
2
)
{
(
void
)
sleep
(
0
);
(
void
)
sleep
(
0
);
}
}
free
(
ctx
);
free
_context
(
ctx
);
#if defined(_WIN32)
#if defined(_WIN32)
(
void
)
WSACleanup
();
(
void
)
WSACleanup
();
#endif // _WIN32
#endif // _WIN32
}
}
struct
mg_context
*
mg_start
(
const
struct
mg_config
*
config
)
{
struct
mg_context
*
mg_start
(
mg_callback_t
user_callback
,
const
char
**
options
)
{
struct
mg_context
*
ctx
,
fake_ctx
;
struct
mg_context
*
ctx
;
const
char
*
name
,
*
value
;
int
i
;
int
i
;
#if defined(_WIN32)
#if defined(_WIN32)
...
@@ -3769,35 +3798,52 @@ struct mg_context * mg_start(const struct mg_config *config) {
...
@@ -3769,35 +3798,52 @@ struct mg_context * mg_start(const struct mg_config *config) {
WSAStartup
(
MAKEWORD
(
2
,
2
),
&
data
);
WSAStartup
(
MAKEWORD
(
2
,
2
),
&
data
);
#endif // _WIN32
#endif // _WIN32
// TODO(lsm): make a copy of the config
// Allocate context and initialize reasonable general case defaults.
fake_ctx
.
config
=
config
;
// TODO(lsm): do proper error handling here.
ctx
=
calloc
(
1
,
sizeof
(
*
ctx
));
ctx
->
user_callback
=
user_callback
;
ctx
->
config
[
DOCUMENT_ROOT
]
=
mg_strdup
(
"."
);
ctx
->
config
[
LISTENING_PORTS
]
=
mg_strdup
(
"8080"
);
ctx
->
config
[
ENABLE_DIRECTORY_LISTING
]
=
mg_strdup
(
"yes"
);
ctx
->
config
[
ENABLE_KEEP_ALIVE
]
=
mg_strdup
(
"no"
);
ctx
->
config
[
AUTHENTICATION_DOMAIN
]
=
mg_strdup
(
"mydomain.com"
);
ctx
->
config
[
INDEX_FILES
]
=
mg_strdup
(
"index.html,index.htm,index.cgi"
);
ctx
->
config
[
CGI_EXTENSIONS
]
=
mg_strdup
(
".cgi,.pl,.php"
);
ctx
->
config
[
SSI_EXTENSIONS
]
=
mg_strdup
(
".shtml,.shtm"
);
ctx
->
config
[
NUM_THREADS
]
=
mg_strdup
(
"10"
);
while
((
name
=
*
options
++
)
!=
NULL
)
{
if
((
i
=
get_option_index
(
name
))
==
-
1
)
{
cry
(
fc
(
ctx
),
"Invalid option: %s"
,
name
);
free_context
(
ctx
);
return
NULL
;
}
else
if
((
value
=
*
options
++
)
==
NULL
)
{
cry
(
fc
(
ctx
),
"%s: option value cannot be NULL"
,
name
);
free_context
(
ctx
);
return
NULL
;
}
if
(
ctx
->
config
[
i
]
!=
NULL
)
{
free
(
ctx
->
config
[
i
]);
}
ctx
->
config
[
i
]
=
mg_strdup
(
value
);
}
if
(
config
->
listening_ports
==
NULL
||
if
(
!
verify_document_root
(
ctx
))
{
config
->
num_threads
==
NULL
||
free_context
(
ctx
);
config
->
auth_domain
==
NULL
)
{
cry
(
fc
(
&
fake_ctx
),
"Please specify "
"num_threads, listening_ports, auth_domain"
);
return
NULL
;
}
else
if
(
config
->
document_root
!=
NULL
&&
verify_document_root
(
&
fake_ctx
,
config
->
document_root
)
!=
MG_TRUE
)
{
cry
(
fc
(
&
fake_ctx
),
"Invalid root directory:
\"
%s
\"
"
,
config
->
document_root
);
return
NULL
;
}
else
if
((
ctx
=
calloc
(
1
,
sizeof
(
*
ctx
)))
==
NULL
)
{
cry
(
fc
(
&
fake_ctx
),
"Cannot allocate mongoose context"
);
return
NULL
;
return
NULL
;
}
}
ctx
->
config
=
config
;
// NOTE(lsm): order is important here. SSL certificates must
// NOTE(lsm): order is important here. SSL certificates must
// be initialized before listening ports. UID must be set last.
// be initialized before listening ports. UID must be set last.
if
(
set_ssl_option
(
ctx
)
==
MG_ERROR
||
if
(
!
set_ssl_option
(
ctx
)
||
set_ports_option
(
ctx
)
==
MG_ERROR
||
!
set_ports_option
(
ctx
)
||
set_gpass_option
(
ctx
)
==
MG_ERROR
||
!
set_gpass_option
(
ctx
)
||
#if !defined(_WIN32)
#if !defined(_WIN32)
set_uid_option
(
ctx
)
==
MG_ERROR
||
!
set_uid_option
(
ctx
)
||
#endif
#endif
set_acl_option
(
ctx
)
==
MG_ERROR
)
{
!
set_acl_option
(
ctx
)
)
{
free
(
ctx
);
free
_context
(
ctx
);
return
NULL
;
return
NULL
;
}
}
...
@@ -3816,7 +3862,7 @@ struct mg_context * mg_start(const struct mg_config *config) {
...
@@ -3816,7 +3862,7 @@ struct mg_context * mg_start(const struct mg_config *config) {
start_thread
(
ctx
,
(
mg_thread_func_t
)
master_thread
,
ctx
);
start_thread
(
ctx
,
(
mg_thread_func_t
)
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
(
start_thread
(
ctx
,
(
mg_thread_func_t
)
worker_thread
,
ctx
)
!=
0
)
{
if
(
start_thread
(
ctx
,
(
mg_thread_func_t
)
worker_thread
,
ctx
)
!=
0
)
{
cry
(
fc
(
ctx
),
"Cannot start worker thread: %d"
,
ERRNO
);
cry
(
fc
(
ctx
),
"Cannot start worker thread: %d"
,
ERRNO
);
}
else
{
}
else
{
...
...
mongoose.h
View file @
546bec33
...
@@ -29,8 +29,7 @@ struct mg_context; // Handle for the HTTP service itself
...
@@ -29,8 +29,7 @@ struct mg_context; // Handle for the HTTP service itself
struct
mg_connection
;
// Handle for the individual connection
struct
mg_connection
;
// Handle for the individual connection
// This structure contains full information about the HTTP request.
// This structure contains information about the HTTP request.
// It is passed to the user-specified callback function as a parameter.
struct
mg_request_info
{
struct
mg_request_info
{
char
*
request_method
;
// "GET", "POST", etc
char
*
request_method
;
// "GET", "POST", etc
char
*
uri
;
// URL-decoded URI
char
*
uri
;
// URL-decoded URI
...
@@ -40,7 +39,7 @@ struct mg_request_info {
...
@@ -40,7 +39,7 @@ struct mg_request_info {
char
*
log_message
;
// Mongoose error log message
char
*
log_message
;
// Mongoose error log message
long
remote_ip
;
// Client's IP address
long
remote_ip
;
// Client's IP address
int
remote_port
;
// Client's port
int
remote_port
;
// Client's port
int
status_code
;
// HTTP status code
int
status_code
;
// HTTP
reply
status code
int
is_ssl
;
// 1 if SSL-ed, 0 if not
int
is_ssl
;
// 1 if SSL-ed, 0 if not
int
num_headers
;
// Number of headers
int
num_headers
;
// Number of headers
struct
mg_header
{
struct
mg_header
{
...
@@ -49,65 +48,57 @@ struct mg_request_info {
...
@@ -49,65 +48,57 @@ struct mg_request_info {
}
http_headers
[
64
];
// Maximum 64 headers
}
http_headers
[
64
];
// Maximum 64 headers
};
};
// User-defined handler function. It must return MG_SUCCESS or MG_ERROR.
// Various events on which user-defined function is called by Mongoose.
//
enum
mg_event
{
// If handler returns MG_SUCCESS, that means that handler has processed the
MG_NEW_REQUEST
,
// New HTTP request has arrived from the client
// request by sending appropriate HTTP reply to the client. Mongoose treats
MG_HTTP_ERROR
,
// HTTP error must be returned to the client
// the request as served.
MG_EVENT_LOG
,
// Mongoose logs an event, request_info.log_message
//
MG_INIT_SSL
,
// Mongoose initializes SSL. Instead of mg_connection *,
// If callback returns MG_ERROR, that means that callback has not processed
// SSL context is passed to the callback function.
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
//
// NOTE: ssl_password_handler must have the following prototype:
// int (*)(char *, int, int, void *)
// Refer to OpenSSL documentation for more details.
enum
mg_error_t
{
MG_ERROR
,
MG_SUCCESS
,
MG_NOT_FOUND
,
MG_BUFFER_TOO_SMALL
};
typedef
enum
mg_error_t
(
*
mg_callback_t
)(
struct
mg_connection
*
,
const
struct
mg_request_info
*
);
// This structure describes Mongoose configuration.
struct
mg_config
{
char
*
document_root
;
char
*
index_files
;
char
*
ssl_certificate
;
char
*
listening_ports
;
char
*
cgi_extensions
;
char
*
cgi_interpreter
;
char
*
cgi_environment
;
char
*
ssi_extensions
;
char
*
auth_domain
;
char
*
protect
;
char
*
global_passwords_file
;
char
*
put_delete_passwords_file
;
char
*
access_log_file
;
char
*
error_log_file
;
char
*
acl
;
char
*
uid
;
char
*
mime_types
;
char
*
enable_directory_listing
;
char
*
num_threads
;
mg_callback_t
new_request_handler
;
mg_callback_t
http_error_handler
;
mg_callback_t
event_log_handler
;
mg_callback_t
ssl_password_handler
;
};
};
// Prototype for the user-defined function. Mongoose calls this function
// Start the web server.
// on every event mentioned above.
//
// Parameters:
// event: which event has been triggered.
// conn: opaque connection handler. Could be used to read, write data to the
// client, etc. See functions below that accept "mg_connection *".
// request_info: Information about HTTP request.
//
//
// This must be the first function called by the application.
// Return:
// It creates a serving thread, and returns a context structure that
// If handler returns non-NULL, that means that handler has processed the
// can be used to stop the server.
// request by sending appropriate HTTP reply to the client. Mongoose treats
// After calling mg_start(), configuration data must not be changed.
// the request as served.
struct
mg_context
*
mg_start
(
const
struct
mg_config
*
);
// If callback returns NULL, that means that callback has not processed
// the request. Handler must not send any data to the client in this case.
// Mongoose proceeds with request handling as if nothing happened.
typedef
void
*
(
*
mg_callback_t
)(
enum
mg_event
event
,
struct
mg_connection
*
conn
,
struct
mg_request_info
*
request_info
);
// Start web server.
//
// Parameters:
// callback: user defined event handling function or NULL.
// options: NULL terminated list of option_name, option_value pairs that
// specify Mongoose configuration parameters.
//
// Example:
// const char *options[] = {
// "document_root", "/var/www",
// "listening_ports", "80,443s",
// NULL
// };
// struct mg_context *ctx = mg_start(&my_func, options);
//
// Please refer to http://code.google.com/p/mongoose/wiki/MongooseManual
// for the list of valid option and their possible values.
//
// Return:
// web server context, or NULL on error.
struct
mg_context
*
mg_start
(
mg_callback_t
callback
,
const
char
**
options
);
// Stop the web server.
// Stop the web server.
...
@@ -118,6 +109,19 @@ struct mg_context *mg_start(const struct mg_config *);
...
@@ -118,6 +109,19 @@ struct mg_context *mg_start(const struct mg_config *);
void
mg_stop
(
struct
mg_context
*
);
void
mg_stop
(
struct
mg_context
*
);
// Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing
// configuration at run time.
// If given parameter name is not valid, NULL is returned. For valid
// names, return value is guaranteed to be non-NULL. If parameter is not
// set, zero-length string is returned.
const
char
*
mg_get_option
(
const
struct
mg_context
*
ctx
,
const
char
*
name
);
// Return array of valid configuration options.
const
char
**
mg_get_valid_option_names
(
void
);
// Add, edit or delete the entry in the passwords file.
// Add, edit or delete the entry in the passwords file.
//
//
// This function allows an application to manipulate .htpasswd files on the
// This function allows an application to manipulate .htpasswd files on the
...
@@ -129,9 +133,9 @@ void mg_stop(struct mg_context *);
...
@@ -129,9 +133,9 @@ void mg_stop(struct mg_context *);
// If password is NULL, entry is deleted.
// If password is NULL, entry is deleted.
//
//
// Return:
// Return:
//
MG_ERROR, MG_SUCCESS
//
1 on success, 0 on error.
enum
mg_error_
t
mg_modify_passwords_file
(
struct
mg_context
*
ctx
,
in
t
mg_modify_passwords_file
(
struct
mg_context
*
ctx
,
const
char
*
file_name
,
const
char
*
user
,
const
char
*
password
);
const
char
*
passwords_
file_name
,
const
char
*
user
,
const
char
*
password
);
// Send data to the client.
// Send data to the client.
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
size_t
len
);
int
mg_write
(
struct
mg_connection
*
,
const
void
*
buf
,
size_t
len
);
...
@@ -160,32 +164,35 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
...
@@ -160,32 +164,35 @@ const char *mg_get_header(const struct mg_connection *, const char *name);
// Get a value of particular form variable.
// Get a value of particular form variable.
//
//
// Either request_info->query_string or read POST data can be scanned.
// Parameters:
// mg_get_qsvar() is convenience method to get variable from the query string.
// data: pointer to form-uri-encoded buffer. This could be either POST data,
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// or request_info.query_string.
// failure, dst[0] == '\0'.
// data_len: length of the encoded data.
// var_name: variable name to decode from the buffer
// buf: destination buffer for the decoded variable
// buf_len: length of the destination buffer
//
//
// Return:
// Return:
//
MG_SUCCESS Variable value was successfully copied in the buffer
.
//
On success, length of the decoded variable
.
//
MG_NOT_FOUND Requested variable not found
.
//
On error, -1 (variable not found, or destination buffer is too small)
.
//
MG_BUFFER_TOO_SMALL Destination buffer is too small to hold the value.
//
enum
mg_error_t
mg_get_var
(
const
char
*
data
,
size_t
data_len
,
// Destination buffer is guaranteed to be '\0' - terminated. In case of
const
char
*
var_name
,
char
*
buf
,
size_t
buf_len
);
// failure, dst[0] == '\0'.
enum
mg_error_t
mg_get_qsvar
(
const
struct
mg_request_info
*
,
int
mg_get_var
(
const
char
*
data
,
size_t
data_len
,
const
char
*
var_name
,
char
*
buf
,
size_t
buf_len
);
const
char
*
var_name
,
char
*
buf
,
size_t
buf_len
);
// Fetch value of certain cookie variable into the destination buffer.
// Fetch value of certain cookie variable into the destination buffer.
//
//
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// Destination buffer is guaranteed to be '\0' - terminated. In case of
// failure, dst[0] == '\0'. Note that RFC allows many occurences of the same
// failure, dst[0] == '\0'. Note that RFC allows many occur
r
ences of the same
// parameter. This function returns only first occur
a
nce.
// parameter. This function returns only first occur
re
nce.
//
//
// Return:
// Return:
//
MG_SUCCESS Cookie parameter was successfully copied in the buffer
.
//
On success, value length
.
//
MG_NOT_FOUND E
ither "Cookie:" header is not present at all, or the
//
On error, -1 (e
ither "Cookie:" header is not present at all, or the
//
requested parameter is not found.
//
requested parameter is not found, or destination buffer is too small
//
MG_BUFFER_TOO_SMALL Destination buffer is too small to hold the value
.
//
to hold the value)
.
enum
mg_error_
t
mg_get_cookie
(
const
struct
mg_connection
*
,
in
t
mg_get_cookie
(
const
struct
mg_connection
*
,
const
char
*
cookie_name
,
char
*
buf
,
size_t
buf_len
);
const
char
*
cookie_name
,
char
*
buf
,
size_t
buf_len
);
...
...
test/embed.c
View file @
546bec33
/*
// Copyright (c) 2004-2009 Sergey Lyubka
* Copyright (c) 2004-2009 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
// in the Software without restriction, including without limitation the rights
* in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
* copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
* furnished to do so, subject to the following conditions:
//
*
// The above copyright notice and this permission notice shall be included in
* The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
* all copies or substantial portions of the Software.
//
*
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
* THE SOFTWARE.
//
*
// Unit test for the mongoose web server. Tests embedded API.
* $Id: embed.c 471 2009-08-30 14:30:21Z valenok $
* Unit test for the mongoose web server. Tests embedded API.
*/
#include <stdlib.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include "mongoose.h"
#include "mongoose.h"
#if !defined(LISTENING_PORT)
#if !defined(LISTENING_PORT)
#define LISTENING_PORT "23456"
#define LISTENING_PORT "23456"
#endif
/* !LISTENING_PORT */
#endif
static
const
char
*
standard_reply
=
"HTTP/1.1 200 OK
\r\n
"
static
const
char
*
standard_reply
=
"HTTP/1.1 200 OK
\r\n
"
"Content-Type: text/plain
\r\n
"
"Content-Type: text/plain
\r\n
"
"Connection: close
\r\n\r\n
"
;
"Connection: close
\r\n\r\n
"
;
static
void
static
void
test_get_var
(
struct
mg_connection
*
conn
,
test_get_var
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
const
struct
mg_request_info
*
ri
)
{
void
*
user_data
)
char
*
var
,
*
buf
;
{
size_t
buf_len
;
char
*
value
;
const
char
*
cl
;
int
var_len
;
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_printf
(
conn
,
"%s"
,
standard_reply
);
value
=
mg_get_var
(
conn
,
"my_var"
);
buf_len
=
0
;
if
(
value
!=
NULL
)
{
var
=
buf
=
NULL
;
mg_printf
(
conn
,
"Value: [%s]
\n
"
,
value
);
cl
=
mg_get_header
(
conn
,
"Content-Length"
);
mg_printf
(
conn
,
"Value size: [%u]
\n
"
,
(
unsigned
)
strlen
(
value
));
mg_printf
(
conn
,
"cl: %p
\n
"
,
cl
);
free
(
value
);
if
(
!
strcmp
(
ri
->
request_method
,
"POST"
)
&&
cl
!=
NULL
)
{
}
buf_len
=
atoi
(
cl
);
buf
=
malloc
(
buf_len
);
mg_read
(
conn
,
buf
,
buf_len
);
}
else
if
(
ri
->
query_string
!=
NULL
)
{
buf_len
=
strlen
(
ri
->
query_string
);
buf
=
malloc
(
buf_len
+
1
);
strcpy
(
buf
,
ri
->
query_string
);
}
var
=
malloc
(
buf_len
+
1
);
var_len
=
mg_get_var
(
buf
,
buf_len
,
"my_var"
,
var
,
buf_len
+
1
);
mg_printf
(
conn
,
"Value: [%s]
\n
"
,
var
);
mg_printf
(
conn
,
"Value size: [%d]
\n
"
,
var_len
);
free
(
buf
);
free
(
var
);
}
}
static
void
static
void
test_get_header
(
struct
mg_connection
*
conn
,
test_get_header
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
const
struct
mg_request_info
*
ri
)
{
void
*
user_data
)
{
const
char
*
value
;
const
char
*
value
;
int
i
;
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_printf
(
conn
,
"%s"
,
standard_reply
);
printf
(
"HTTP headers: %d
\n
"
,
ri
->
num_headers
);
{
for
(
i
=
0
;
i
<
ri
->
num_headers
;
i
++
)
{
int
i
;
printf
(
"[%s]: [%s]
\n
"
,
ri
->
http_headers
[
i
].
name
,
ri
->
http_headers
[
i
].
value
);
printf
(
"HTTP headers: %d
\n
"
,
ri
->
num_headers
);
}
for
(
i
=
0
;
i
<
ri
->
num_headers
;
i
++
)
printf
(
"[%s]: [%s]
\n
"
,
ri
->
http_headers
[
i
].
name
,
ri
->
http_headers
[
i
].
value
);
}
value
=
mg_get_header
(
conn
,
"Host"
);
value
=
mg_get_header
(
conn
,
"Host"
);
if
(
value
!=
NULL
)
if
(
value
!=
NULL
)
{
mg_printf
(
conn
,
"Value: [%s]"
,
value
);
mg_printf
(
conn
,
"Value: [%s]"
,
value
);
}
}
}
static
void
static
void
test_get_request_info
(
struct
mg_connection
*
conn
,
test_get_ri
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
const
struct
mg_request_info
*
ri
)
{
void
*
user_data
)
{
int
i
;
int
i
;
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_printf
(
conn
,
"%s"
,
standard_reply
);
...
@@ -88,110 +95,79 @@ test_get_ri(struct mg_connection *conn, const struct mg_request_info *ri,
...
@@ -88,110 +95,79 @@ test_get_ri(struct mg_connection *conn, const struct mg_request_info *ri,
mg_printf
(
conn
,
"URI: [%s]
\n
"
,
ri
->
uri
);
mg_printf
(
conn
,
"URI: [%s]
\n
"
,
ri
->
uri
);
mg_printf
(
conn
,
"HTTP version: [%s]
\n
"
,
ri
->
http_version
);
mg_printf
(
conn
,
"HTTP version: [%s]
\n
"
,
ri
->
http_version
);
for
(
i
=
0
;
i
<
ri
->
num_headers
;
i
++
)
for
(
i
=
0
;
i
<
ri
->
num_headers
;
i
++
)
{
mg_printf
(
conn
,
"HTTP header [%s]: [%s]
\n
"
,
mg_printf
(
conn
,
"HTTP header [%s]: [%s]
\n
"
,
ri
->
http_headers
[
i
].
name
,
ri
->
http_headers
[
i
].
name
,
ri
->
http_headers
[
i
].
value
);
ri
->
http_headers
[
i
].
value
);
}
mg_printf
(
conn
,
"Query string: [%s]
\n
"
,
mg_printf
(
conn
,
"Query string: [%s]
\n
"
,
ri
->
query_string
?
ri
->
query_string
:
""
);
ri
->
query_string
?
ri
->
query_string
:
""
);
mg_printf
(
conn
,
"POST data: [%.*s]
\n
"
,
ri
->
post_data_len
,
ri
->
post_data
);
mg_printf
(
conn
,
"Remote IP: [%lu]
\n
"
,
ri
->
remote_ip
);
mg_printf
(
conn
,
"Remote IP: [%lu]
\n
"
,
ri
->
remote_ip
);
mg_printf
(
conn
,
"Remote port: [%d]
\n
"
,
ri
->
remote_port
);
mg_printf
(
conn
,
"Remote port: [%d]
\n
"
,
ri
->
remote_port
);
mg_printf
(
conn
,
"Remote user: [%s]
\n
"
,
mg_printf
(
conn
,
"Remote user: [%s]
\n
"
,
ri
->
remote_user
?
ri
->
remote_user
:
""
);
ri
->
remote_user
?
ri
->
remote_user
:
""
);
}
}
static
void
static
void
test_error
(
struct
mg_connection
*
conn
,
test_error
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
const
struct
mg_request_info
*
ri
)
{
void
*
user_data
)
{
const
char
*
value
;
mg_printf
(
conn
,
"HTTP/1.1 %d XX
\r\n
"
mg_printf
(
conn
,
"HTTP/1.1 %d XX
\r\n
"
"Conntection: close
\r\n\r\n
"
,
ri
->
status_code
);
"Conntection: close
\r\n\r\n
"
,
ri
->
status_code
);
mg_printf
(
conn
,
"Error: [%d]"
,
ri
->
status_code
);
mg_printf
(
conn
,
"Error: [%d]"
,
ri
->
status_code
);
}
}
static
void
static
void
test_post
(
struct
mg_connection
*
conn
,
test_user_data
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
const
struct
mg_request_info
*
ri
)
{
void
*
user_data
)
const
char
*
cl
;
{
char
*
buf
;
const
char
*
value
;
int
len
;
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_printf
(
conn
,
"User data: [%d]"
,
*
(
int
*
)
user_data
);
if
(
strcmp
(
ri
->
request_method
,
"POST"
)
==
0
&&
(
cl
=
mg_get_header
(
conn
,
"Content-Length"
))
!=
NULL
)
{
len
=
atoi
(
cl
);
if
((
buf
=
malloc
(
len
))
!=
NULL
)
{
mg_write
(
conn
,
buf
,
len
);
free
(
buf
);
}
}
}
}
static
void
static
const
struct
test_config
{
test_protect
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
enum
mg_event
event
;
void
*
user_data
)
const
char
*
uri
;
{
void
(
*
func
)(
struct
mg_connection
*
,
const
struct
mg_request_info
*
);
const
char
*
allowed_user
=
*
(
char
**
)
user_data
;
}
test_config
[]
=
{
const
char
*
remote_user
=
ri
->
remote_user
;
{
MG_NEW_REQUEST
,
"/test_get_header"
,
&
test_get_header
},
int
allowed
;
{
MG_NEW_REQUEST
,
"/test_get_var"
,
&
test_get_var
},
{
MG_NEW_REQUEST
,
"/test_get_request_info"
,
&
test_get_request_info
},
allowed
=
remote_user
!=
NULL
&&
!
strcmp
(
allowed_user
,
remote_user
);
{
MG_NEW_REQUEST
,
"/test_post"
,
&
test_post
},
{
MG_HTTP_ERROR
,
""
,
&
test_error
},
*
(
long
*
)
user_data
=
allowed
?
1
:
0
;
{
0
,
NULL
,
NULL
}
};
static
void
*
callback
(
enum
mg_event
event
,
struct
mg_connection
*
conn
,
struct
mg_request_info
*
request_info
)
{
int
i
;
for
(
i
=
0
;
test_config
[
i
].
uri
!=
NULL
;
i
++
)
{
if
(
event
==
test_config
[
i
].
event
&&
(
event
==
MG_HTTP_ERROR
||
!
strcmp
(
request_info
->
uri
,
test_config
[
i
].
uri
)))
{
test_config
[
i
].
func
(
conn
,
request_info
);
return
"processed"
;
}
}
return
NULL
;
}
}
static
void
int
main
(
void
)
{
test_post
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
void
*
user_data
)
{
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_write
(
conn
,
ri
->
post_data
,
ri
->
post_data_len
);
}
static
void
test_put
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
void
*
user_data
)
{
mg_printf
(
conn
,
"%s"
,
standard_reply
);
mg_write
(
conn
,
ri
->
post_data
,
ri
->
post_data_len
);
}
static
void
test_remove_callback
(
struct
mg_connection
*
conn
,
const
struct
mg_request_info
*
ri
,
void
*
user_data
)
{
struct
mg_context
*
ctx
=
(
struct
mg_context
*
)
user_data
;
const
char
*
uri_regex
=
"/foo/*"
;
mg_printf
(
conn
,
"%sRemoving callbacks bound to [%s]"
,
standard_reply
,
uri_regex
);
/* Un-bind bound callback */
mg_set_uri_callback
(
ctx
,
uri_regex
,
NULL
,
NULL
);
}
int
main
(
void
)
{
int
user_data
=
1234
;
struct
mg_context
*
ctx
;
struct
mg_context
*
ctx
;
const
char
*
options
[]
=
{
"listening_ports"
,
LISTENING_PORT
,
NULL
};
ctx
=
mg_start
();
ctx
=
mg_start
(
callback
,
options
);
mg_set_option
(
ctx
,
"ports"
,
LISTENING_PORT
);
pause
();
return
0
;
mg_set_uri_callback
(
ctx
,
"/test_get_header"
,
&
test_get_header
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/test_get_var"
,
&
test_get_var
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/test_get_request_info"
,
&
test_get_ri
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/foo/*"
,
&
test_get_ri
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/test_user_data"
,
&
test_user_data
,
&
user_data
);
mg_set_uri_callback
(
ctx
,
"/p"
,
&
test_post
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/put"
,
&
test_put
,
NULL
);
mg_set_uri_callback
(
ctx
,
"/test_remove_callback"
,
&
test_remove_callback
,
ctx
);
mg_set_error_callback
(
ctx
,
404
,
&
test_error
,
NULL
);
mg_set_error_callback
(
ctx
,
0
,
&
test_error
,
NULL
);
mg_set_auth_callback
(
ctx
,
"/foo/secret"
,
&
test_protect
,
(
void
*
)
"joe"
);
for
(;;)
(
void
)
getchar
();
}
}
test/test.pl
View file @
546bec33
...
@@ -146,7 +146,7 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'embedded') {
...
@@ -146,7 +146,7 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'embedded') {
}
}
# Make sure we load config file if no options are given
# Make sure we load config file if no options are given
write_file
(
$config
,
"
ports 12345\naccess_log
access.log\n"
);
write_file
(
$config
,
"
listening_ports 12345\naccess_log_file
access.log\n"
);
spawn
(
$exe
);
spawn
(
$exe
);
my
$saved_port
=
$port
;
my
$saved_port
=
$port
;
$port
=
12345
;
$port
=
12345
;
...
@@ -156,11 +156,13 @@ unlink $config;
...
@@ -156,11 +156,13 @@ unlink $config;
kill_spawned_child
();
kill_spawned_child
();
# Spawn the server on port $port
# Spawn the server on port $port
my
$cmd
=
"$exe -ports $port -access_log access.log -error_log debug.log "
.
my
$cmd
=
"$exe -listening_ports $port -access_log_file access.log "
.
"-cgi_env CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz "
.
"-error_log_file debug.log "
.
"-mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo "
.
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz "
.
"-root $root,/aiased=/etc/,/ta=$test_dir"
;
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo "
.
$cmd
.=
' -cgi_interp perl'
if
on_windows
();
'-put_delete_passwords_file test/passfile '
.
"-document_root $root,/aiased=/etc/,/ta=$test_dir"
;
$cmd
.=
' -cgi_interpreter perl'
if
on_windows
();
spawn
(
$cmd
);
spawn
(
$cmd
);
# Try to overflow: Send very long request
# Try to overflow: Send very long request
...
@@ -349,15 +351,12 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
...
@@ -349,15 +351,12 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
$content
=~
/^b:a:\w+$/gs
or
fail
(
"Bad content of the passwd file"
);
$content
=~
/^b:a:\w+$/gs
or
fail
(
"Bad content of the passwd file"
);
unlink
$path
;
unlink
$path
;
kill_spawned_child
();
do_PUT_test
();
do_PUT_test
();
#do_embedded_test();
kill_spawned_child
();
do_embedded_test
();
}
}
sub
do_PUT_test
{
sub
do_PUT_test
{
$cmd
.=
' -auth_PUT test/passfile'
;
spawn
(
$cmd
);
my
$auth_header
=
"Authorization: Digest username=guest, "
.
my
$auth_header
=
"Authorization: Digest username=guest, "
.
"realm=mydomain.com, nonce=1145872809, uri=/put.txt, "
.
"realm=mydomain.com, nonce=1145872809, uri=/put.txt, "
.
"response=896327350763836180c61d87578037d9, qop=auth, "
.
"response=896327350763836180c61d87578037d9, qop=auth, "
.
...
@@ -379,16 +378,14 @@ sub do_PUT_test {
...
@@ -379,16 +378,14 @@ sub do_PUT_test {
o
(
"PUT /put.txt HTTP/1.0\nExpect: 100-continue\nContent-Length: 4\n"
.
o
(
"PUT /put.txt HTTP/1.0\nExpect: 100-continue\nContent-Length: 4\n"
.
"$auth_header\nabcd"
,
"$auth_header\nabcd"
,
"HTTP/1.1 100 Continue.+HTTP/1.1 200"
,
'PUT 100-Continue'
);
"HTTP/1.1 100 Continue.+HTTP/1.1 200"
,
'PUT 100-Continue'
);
kill_spawned_child
();
}
}
sub
do_embedded_test
{
sub
do_embedded_test
{
my
$cmd
=
"cc -o $embed_exe $root/embed.c mongoose.c -I. "
.
my
$cmd
=
"cc -
W -Wall -
o $embed_exe $root/embed.c mongoose.c -I. "
.
"-
DNO_SSL -l
pthread -DLISTENING_PORT=\\\"$port\\\""
;
"-pthread -DLISTENING_PORT=\\\"$port\\\""
;
if
(
on_windows
())
{
if
(
on_windows
())
{
$cmd
=
"cl $root/embed.c mongoose.c /I. /nologo "
.
$cmd
=
"cl $root/embed.c mongoose.c /I. /nologo "
.
"/DNO_SSL /DLISTENING_PORT=\\\"$port\\\" "
.
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib "
;
"/link /out:$embed_exe.exe ws2_32.lib "
;
}
}
print
$cmd
,
"\n"
;
print
$cmd
,
"\n"
;
system
(
$cmd
)
==
0
or
fail
(
"Cannot compile embedded unit test"
);
system
(
$cmd
)
==
0
or
fail
(
"Cannot compile embedded unit test"
);
...
@@ -415,30 +412,24 @@ sub do_embedded_test {
...
@@ -415,30 +412,24 @@ sub do_embedded_test {
# + in form data MUST be decoded to space
# + in form data MUST be decoded to space
o
(
"POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n"
.
o
(
"POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n"
.
"my_var=b+c"
,
'Value: \[b c\]'
,
'mg_get_var
7
'
,
0
);
"my_var=b+c"
,
'Value: \[b c\]'
,
'mg_get_var
9
'
,
0
);
# Test that big POSTed vars are not truncated
# Test that big POSTed vars are not truncated
my
$my_var
=
'x'
x
64000
;
my
$my_var
=
'x'
x
64000
;
o
(
"POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n"
.
o
(
"POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n"
.
"my_var=$my_var"
,
'Value size: \[64000\]'
,
'mg_get_var 8'
,
0
);
"my_var=$my_var"
,
'Value size: \[64000\]'
,
'mg_get_var 10'
,
0
);
# Test PUT
o
(
"PUT /put HTTP/1.0\nContent-Length: 3\n\nabc"
,
'\nabc$'
,
'put callback'
,
0
);
o
(
"POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n"
.
o
(
"POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n"
.
"Content-Length: 3\n\na=b"
,
"Content-Length: 3\n\na=b"
,
'Method: \[POST\].URI: \[/test_get_request_info\].'
.
'Method: \[POST\].URI: \[/test_get_request_info\].'
.
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'
.
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'
.
'HTTP header \[Content-Length\]: \[3\].'
.
'HTTP header \[Content-Length\]: \[3\].'
.
'Query string: \[xx=yy\].
POST data: \[a=b\].
'
.
'Query string: \[xx=yy\].'
.
'Remote IP: \[\d+\].Remote port: \[\d+\].'
.
'Remote IP: \[\d+\].Remote port: \[\d+\].'
.
'Remote user: \[\]'
'Remote user: \[\]'
,
'request_info'
,
0
);
,
'request_info'
,
0
);
o
(
"GET /not_exist HTTP/1.0\n\n"
,
'Error: \[404\]'
,
'404 handler'
,
0
);
o
(
"GET /not_exist HTTP/1.0\n\n"
,
'Error: \[404\]'
,
'404 handler'
,
0
);
o
(
"bad request\n\n"
,
'Error: \[400\]'
,
'* error handler'
,
0
);
o
(
"bad request\n\n"
,
'Error: \[400\]'
,
'* error handler'
,
0
);
o
(
"GET /test_user_data HTTP/1.0\n\n"
,
'User data: \[1234\]'
,
'user data in callback'
,
0
);
# o("GET /foo/secret HTTP/1.0\n\n",
# o("GET /foo/secret HTTP/1.0\n\n",
# '401 Unauthorized', 'mg_protect_uri', 0);
# '401 Unauthorized', 'mg_protect_uri', 0);
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
...
@@ -446,12 +437,6 @@ sub do_embedded_test {
...
@@ -446,12 +437,6 @@ sub do_embedded_test {
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
# '200 OK', 'mg_protect_uri (joe)', 0);
# '200 OK', 'mg_protect_uri (joe)', 0);
# Test un-binding the URI
o
(
"GET /foo/bar HTTP/1.0\n\n"
,
'HTTP/1.1 200 OK'
,
'/foo bound'
,
0
);
o
(
"GET /test_remove_callback HTTP/1.0\n\n"
,
'Removing callbacks'
,
'Callback removal'
,
0
);
o
(
"GET /foo/bar HTTP/1.0\n\n"
,
'HTTP/1.1 404'
,
'/foo unbound'
,
0
);
kill_spawned_child
();
kill_spawned_child
();
}
}
...
...
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