mirror of
https://github.com/cesanta/mongoose.git
synced 2025-01-14 01:38:01 +08:00
API change for mg_start: most binary compatible across releases.
This commit is contained in:
parent
0b1e621c36
commit
546bec333c
376
main.c
376
main.c
@ -57,239 +57,213 @@ static int exit_flag; /* Program termination flag */
|
||||
#define CONFIG_FILE "mongoose.conf"
|
||||
#endif /* !CONFIG_FILE */
|
||||
|
||||
static void
|
||||
signal_handler(int sig_num)
|
||||
{
|
||||
#define MAX_OPTIONS 40
|
||||
|
||||
static void signal_handler(int sig_num) {
|
||||
#if !defined(_WIN32)
|
||||
if (sig_num == SIGCHLD) {
|
||||
do {
|
||||
} while (waitpid(-1, &sig_num, WNOHANG) > 0);
|
||||
} else
|
||||
if (sig_num == SIGCHLD) {
|
||||
do {
|
||||
} while (waitpid(-1, &sig_num, WNOHANG) > 0);
|
||||
} else
|
||||
#endif /* !_WIN32 */
|
||||
{
|
||||
exit_flag = sig_num;
|
||||
}
|
||||
{
|
||||
exit_flag = sig_num;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit the passwords file.
|
||||
*/
|
||||
static int
|
||||
mg_edit_passwords(const char *fname, const char *domain,
|
||||
const char *user, const char *pass)
|
||||
{
|
||||
struct mg_context *ctx;
|
||||
struct mg_config config;
|
||||
int retval;
|
||||
static int mg_edit_passwords(const char *fname, const char *domain,
|
||||
const char *user, const char *pass) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"authentication_domain", NULL, NULL};
|
||||
int success;
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.auth_domain = (char *) domain;
|
||||
config.num_threads = "0";
|
||||
config.listening_ports = "";
|
||||
ctx = mg_start(&config);
|
||||
retval = mg_modify_passwords_file(ctx, fname, user, pass);
|
||||
mg_stop(ctx);
|
||||
options[1] = domain;
|
||||
ctx = mg_start(NULL, options);
|
||||
success = mg_modify_passwords_file(ctx, fname, user, pass);
|
||||
mg_stop(ctx);
|
||||
|
||||
return (retval);
|
||||
return success;
|
||||
}
|
||||
|
||||
#define OFFSET(x) offsetof(struct mg_config, x)
|
||||
static void show_usage_and_exit(void) {
|
||||
const char **names;
|
||||
int i, len;
|
||||
|
||||
static struct option_descriptor {
|
||||
const char *name;
|
||||
const char *description;
|
||||
size_t offset;
|
||||
} known_options[] = {
|
||||
{"root", "\tWeb root directory", OFFSET(document_root)},
|
||||
{"index_files", "Index files", OFFSET(index_files)},
|
||||
{"ssl_cert", "SSL certificate file", OFFSET(ssl_certificate)},
|
||||
{"ports", "Listening ports", OFFSET(listening_ports)},
|
||||
{"dir_list", "Directory listing", OFFSET(enable_directory_listing)},
|
||||
{"protect", "URI to htpasswd mapping", OFFSET(protect)},
|
||||
{"cgi_ext", "CGI extensions", OFFSET(cgi_extensions)},
|
||||
{"cgi_interp", "CGI interpreter to use", OFFSET(cgi_interpreter)},
|
||||
{"cgi_env", "Custom CGI enviroment variables", OFFSET(cgi_environment)},
|
||||
{"ssi_ext", "SSI extensions", OFFSET(ssi_extensions)},
|
||||
{"auth_realm", "Authentication domain name", OFFSET(auth_domain)},
|
||||
{"auth_gpass", "Global passwords file", OFFSET(global_passwords_file)},
|
||||
{"auth_PUT", "PUT,DELETE auth file", OFFSET(put_delete_passwords_file)},
|
||||
{"uid", "\tRun as user", OFFSET(uid)},
|
||||
{"access_log", "Access log file", OFFSET(access_log_file)},
|
||||
{"error_log", "Error log file", OFFSET(error_log_file)},
|
||||
{"acl", "\tAllow/deny IP addresses/subnets", OFFSET(acl)},
|
||||
{"num_threads", "Threads to spawn", OFFSET(num_threads)},
|
||||
{"mime_types", "Extra mime types to use", OFFSET(mime_types)},
|
||||
{NULL, NULL, 0}
|
||||
};
|
||||
fprintf(stderr, "Mongoose version %s (c) Sergey Lyubka\n", mg_version());
|
||||
fprintf(stderr, "Usage:\n");
|
||||
fprintf(stderr, " mongoose -A <htpasswd_file> <realm> <user> <passwd>\n");
|
||||
fprintf(stderr, " mongoose <config_file>\n");
|
||||
fprintf(stderr, " mongoose [-option value ...]\n");
|
||||
fprintf(stderr, "OPTIONS:\n ");
|
||||
|
||||
static void
|
||||
show_usage_and_exit(const struct mg_config *config)
|
||||
{
|
||||
const struct option_descriptor *o;
|
||||
const char *value;
|
||||
names = mg_get_valid_option_names();
|
||||
len = 2;
|
||||
for (i = 0; names[i] != NULL; i++) {
|
||||
len += strlen(names[i]) + 1;
|
||||
if (len >= 80) {
|
||||
len = strlen(names[i]) + 3;
|
||||
fprintf(stderr, "%s", "\n ");
|
||||
}
|
||||
fprintf(stderr, "%s%c", names[i], names[i + 1] == NULL ? '\n' : ',');
|
||||
}
|
||||
fprintf(stderr, "See http://code.google.com/p/mongoose/wiki/MongooseManual"
|
||||
" for more details.\n");
|
||||
fprintf(stderr, "Example:\n mongoose -listening_ports 80,443s "
|
||||
"-enable_directory_listing no\n");
|
||||
|
||||
(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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void
|
||||
set_option(struct mg_config *config, const char *name, char *value)
|
||||
{
|
||||
const struct option_descriptor *o;
|
||||
|
||||
for (o = known_options; o->name != NULL; o++)
|
||||
if (strcmp(name, o->name) == 0) {
|
||||
* (char **) ((char *) config + o->offset) = value;
|
||||
break;
|
||||
}
|
||||
|
||||
if (o->name == NULL)
|
||||
show_usage_and_exit(config);
|
||||
static char *sdup(const char *str) {
|
||||
char *p;
|
||||
if ((p = malloc(strlen(str) + 1)) != NULL) {
|
||||
strcpy(p, str);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
FILE *fp;
|
||||
size_t i, line_no = 0;
|
||||
static void set_option(char **options, const char *name, const char *value) {
|
||||
int i;
|
||||
|
||||
/* First find out, which config file to open */
|
||||
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
|
||||
if (argv[i + 1] == NULL)
|
||||
show_usage_and_exit(config);
|
||||
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 (argv[i] != NULL && argv[i + 1] != NULL) {
|
||||
/* More than one non-option arguments are given */
|
||||
show_usage_and_exit(config);
|
||||
} else if (argv[i] != NULL) {
|
||||
/* Just one non-option argument is given, this is config file */
|
||||
config_file = argv[i];
|
||||
} else {
|
||||
/* No config file specified. Look for one where binary lives */
|
||||
if ((p = strrchr(argv[0], DIRSEP)) != 0) {
|
||||
(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 */
|
||||
if (fp == NULL && argv[i] != NULL) {
|
||||
(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));
|
||||
|
||||
if (fp != NULL) {
|
||||
(void) printf("Loading config file %s, "
|
||||
"ignoring command line arguments\n", config_file);
|
||||
|
||||
/* Loop over the lines in config file */
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
|
||||
line_no++;
|
||||
|
||||
/* Ignore empty lines and comments */
|
||||
if (line[0] == '#' || line[0] == '\n')
|
||||
continue;
|
||||
|
||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||
fprintf(stderr, "%s: line %d is invalid\n",
|
||||
config_file, (int) line_no);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* TODO(lsm): free this at some point */
|
||||
p = malloc(strlen(val) + 1);
|
||||
(void) strcpy(p, val);
|
||||
set_option(config, opt, p);
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
} else {
|
||||
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
|
||||
set_option(config, &argv[i][1], argv[i + 1]);
|
||||
}
|
||||
if (i == MAX_OPTIONS - 3) {
|
||||
fprintf(stderr, "%s\n", "Too many options specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct mg_config config;
|
||||
struct mg_context *ctx;
|
||||
static void process_command_line_arguments(char *argv[], char **options) {
|
||||
const char *config_file = CONFIG_FILE;
|
||||
char line[512], opt[512], *vals[100], val[512], path[FILENAME_MAX], *p;
|
||||
FILE *fp;
|
||||
size_t i, line_no = 0;
|
||||
|
||||
/* Initialize configuration with default values */
|
||||
(void) memset(&config, 0, sizeof(config));
|
||||
config.document_root = ".";
|
||||
config.enable_directory_listing = "yes";
|
||||
config.auth_domain = "mydomain.com";
|
||||
config.num_threads = "20";
|
||||
config.index_files = "index.html,index.htm,index.cgi";
|
||||
config.cgi_extensions = ".cgi,.pl,.php";
|
||||
config.ssi_extensions = ".shtml,.shtm";
|
||||
config.listening_ports = "8080";
|
||||
/* First find out, which config file to open */
|
||||
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
|
||||
if (argv[i + 1] == NULL)
|
||||
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)
|
||||
show_usage_and_exit(&config);
|
||||
exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) ==
|
||||
MG_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
if (argv[i] != NULL && argv[i + 1] != NULL) {
|
||||
/* More than one non-option arguments are given */
|
||||
show_usage_and_exit();
|
||||
} else if (argv[i] != NULL) {
|
||||
/* Just one non-option argument is given, this is config file */
|
||||
config_file = argv[i];
|
||||
} else {
|
||||
/* No config file specified. Look for one where binary lives */
|
||||
if ((p = strrchr(argv[0], DIRSEP)) != 0) {
|
||||
snprintf(path, sizeof(path), "%.*s%s",
|
||||
(int) (p - argv[0]) + 1, argv[0], config_file);
|
||||
config_file = path;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
fp = fopen(config_file, "r");
|
||||
|
||||
/* Update config based on command line arguments */
|
||||
process_command_line_arguments(&config, argv);
|
||||
/* If config file was set in command line and open failed, exit */
|
||||
if (fp == NULL && argv[i] != NULL) {
|
||||
fprintf(stderr, "cannot open config file %s: %s\n",
|
||||
config_file, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Setup signal handler: quit on Ctrl-C */
|
||||
/* Reset temporary value holders */
|
||||
(void) memset(vals, 0, sizeof(vals));
|
||||
|
||||
if (fp != NULL) {
|
||||
printf("Loading config file %s, ignoring command line arguments\n",
|
||||
config_file);
|
||||
|
||||
/* Loop over the lines in config file */
|
||||
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||
|
||||
line_no++;
|
||||
|
||||
/* Ignore empty lines and comments */
|
||||
if (line[0] == '#' || line[0] == '\n')
|
||||
continue;
|
||||
|
||||
if (sscanf(line, "%s %[^\r\n#]", opt, val) != 2) {
|
||||
fprintf(stderr, "%s: line %d is invalid\n",
|
||||
config_file, (int) line_no);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
set_option(options, opt, val);
|
||||
}
|
||||
|
||||
(void) fclose(fp);
|
||||
} else {
|
||||
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
|
||||
set_option(options, &argv[i][1], argv[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct mg_context *ctx;
|
||||
char *options[MAX_OPTIONS];
|
||||
int i;
|
||||
|
||||
/* Edit passwords file if -A option is specified */
|
||||
if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'A') {
|
||||
if (argc != 6) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
exit(mg_edit_passwords(argv[2], argv[3], argv[4], argv[5]) ?
|
||||
EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Show usage if -h or --help options are specified */
|
||||
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
|
||||
show_usage_and_exit();
|
||||
}
|
||||
|
||||
/* Update config based on command line arguments */
|
||||
options[0] = NULL;
|
||||
process_command_line_arguments(argv, options);
|
||||
|
||||
/* Setup signal handler: quit on Ctrl-C */
|
||||
#ifndef _WIN32
|
||||
(void) signal(SIGCHLD, signal_handler);
|
||||
signal(SIGCHLD, signal_handler);
|
||||
#endif /* _WIN32 */
|
||||
(void) signal(SIGTERM, signal_handler);
|
||||
(void) signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
signal(SIGINT, signal_handler);
|
||||
|
||||
/* Start Mongoose */
|
||||
if ((ctx = mg_start(&config)) == NULL) {
|
||||
(void) printf("%s\n", "Cannot initialize Mongoose context");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Start Mongoose */
|
||||
ctx = mg_start(NULL, (const char **) options);
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
free(options[i]);
|
||||
}
|
||||
|
||||
(void) printf("Mongoose %s started on port(s) %s "
|
||||
"with web root [%s]\n",
|
||||
mg_version(), config.listening_ports, config.document_root);
|
||||
if (ctx == NULL) {
|
||||
(void) printf("%s\n", "Cannot initialize Mongoose context");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
while (exit_flag == 0)
|
||||
sleep(1);
|
||||
printf("Mongoose %s started on port(s) %s with web root [%s]\n",
|
||||
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);
|
||||
mg_stop(ctx);
|
||||
(void) printf("%s", " done.\n");
|
||||
fflush(stdout);
|
||||
while (exit_flag == 0) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
554
mongoose.c
554
mongoose.c
File diff suppressed because it is too large
Load Diff
165
mongoose.h
165
mongoose.h
@ -29,8 +29,7 @@ struct mg_context; // Handle for the HTTP service itself
|
||||
struct mg_connection; // Handle for the individual connection
|
||||
|
||||
|
||||
// This structure contains full information about the HTTP request.
|
||||
// It is passed to the user-specified callback function as a parameter.
|
||||
// This structure contains information about the HTTP request.
|
||||
struct mg_request_info {
|
||||
char *request_method; // "GET", "POST", etc
|
||||
char *uri; // URL-decoded URI
|
||||
@ -40,7 +39,7 @@ struct mg_request_info {
|
||||
char *log_message; // Mongoose error log message
|
||||
long remote_ip; // Client's IP address
|
||||
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 num_headers; // Number of headers
|
||||
struct mg_header {
|
||||
@ -49,65 +48,57 @@ struct mg_request_info {
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
};
|
||||
|
||||
// User-defined handler function. It must return MG_SUCCESS or MG_ERROR.
|
||||
//
|
||||
// If handler returns MG_SUCCESS, that means that handler has processed the
|
||||
// request by sending appropriate HTTP reply to the client. Mongoose treats
|
||||
// the request as served.
|
||||
//
|
||||
// If callback returns MG_ERROR, 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.
|
||||
//
|
||||
// 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
|
||||
// Various events on which user-defined function is called by Mongoose.
|
||||
enum mg_event {
|
||||
MG_NEW_REQUEST, // New HTTP request has arrived from the client
|
||||
MG_HTTP_ERROR, // HTTP error must be returned to the client
|
||||
MG_EVENT_LOG, // Mongoose logs an event, request_info.log_message
|
||||
MG_INIT_SSL, // Mongoose initializes SSL. Instead of mg_connection *,
|
||||
// SSL context is passed to the callback function.
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
// Start the web server.
|
||||
// Prototype for the user-defined function. Mongoose calls this function
|
||||
// on every event mentioned above.
|
||||
//
|
||||
// This must be the first function called by the application.
|
||||
// It creates a serving thread, and returns a context structure that
|
||||
// can be used to stop the server.
|
||||
// After calling mg_start(), configuration data must not be changed.
|
||||
struct mg_context *mg_start(const struct mg_config *);
|
||||
// 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.
|
||||
//
|
||||
// Return:
|
||||
// If handler returns non-NULL, that means that handler has processed the
|
||||
// request by sending appropriate HTTP reply to the client. Mongoose treats
|
||||
// the request as served.
|
||||
// If 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.
|
||||
@ -118,6 +109,19 @@ struct mg_context *mg_start(const struct mg_config *);
|
||||
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.
|
||||
//
|
||||
// This function allows an application to manipulate .htpasswd files on the
|
||||
@ -129,9 +133,9 @@ void mg_stop(struct mg_context *);
|
||||
// If password is NULL, entry is deleted.
|
||||
//
|
||||
// Return:
|
||||
// MG_ERROR, MG_SUCCESS
|
||||
enum mg_error_t mg_modify_passwords_file(struct mg_context *ctx,
|
||||
const char *file_name, const char *user, const char *password);
|
||||
// 1 on success, 0 on error.
|
||||
int mg_modify_passwords_file(struct mg_context *ctx,
|
||||
const char *passwords_file_name, const char *user, const char *password);
|
||||
|
||||
// Send data to the client.
|
||||
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);
|
||||
|
||||
// Get a value of particular form variable.
|
||||
//
|
||||
// Either request_info->query_string or read POST data can be scanned.
|
||||
// mg_get_qsvar() is convenience method to get variable from the query string.
|
||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
||||
// failure, dst[0] == '\0'.
|
||||
// Parameters:
|
||||
// data: pointer to form-uri-encoded buffer. This could be either POST data,
|
||||
// or request_info.query_string.
|
||||
// data_len: length of the encoded data.
|
||||
// var_name: variable name to decode from the buffer
|
||||
// buf: destination buffer for the decoded variable
|
||||
// buf_len: length of the destination buffer
|
||||
//
|
||||
// Return:
|
||||
// MG_SUCCESS Variable value was successfully copied in the buffer.
|
||||
// MG_NOT_FOUND Requested variable not found.
|
||||
// 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,
|
||||
const char *var_name, char *buf, size_t buf_len);
|
||||
enum mg_error_t mg_get_qsvar(const struct mg_request_info *,
|
||||
// On success, length of the decoded variable.
|
||||
// On error, -1 (variable not found, or destination buffer is too small).
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
||||
// failure, dst[0] == '\0'.
|
||||
int mg_get_var(const char *data, size_t data_len,
|
||||
const char *var_name, char *buf, size_t buf_len);
|
||||
|
||||
// Fetch value of certain cookie variable into the destination buffer.
|
||||
//
|
||||
// Destination buffer is guaranteed to be '\0' - terminated. In case of
|
||||
// failure, dst[0] == '\0'. Note that RFC allows many occurences of the same
|
||||
// parameter. This function returns only first occurance.
|
||||
// failure, dst[0] == '\0'. Note that RFC allows many occurrences of the same
|
||||
// parameter. This function returns only first occurrence.
|
||||
//
|
||||
// Return:
|
||||
// MG_SUCCESS Cookie parameter was successfully copied in the buffer.
|
||||
// MG_NOT_FOUND Either "Cookie:" header is not present at all, or the
|
||||
// requested parameter is not found.
|
||||
// MG_BUFFER_TOO_SMALL Destination buffer is too small to hold the value.
|
||||
enum mg_error_t mg_get_cookie(const struct mg_connection *,
|
||||
// On success, value length.
|
||||
// On error, -1 (either "Cookie:" header is not present at all, or the
|
||||
// requested parameter is not found, or destination buffer is too small
|
||||
// to hold the value).
|
||||
int mg_get_cookie(const struct mg_connection *,
|
||||
const char *cookie_name, char *buf, size_t buf_len);
|
||||
|
||||
|
||||
|
248
test/embed.c
248
test/embed.c
@ -1,85 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2004-2009 Sergey Lyubka
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* $Id: embed.c 471 2009-08-30 14:30:21Z valenok $
|
||||
* Unit test for the mongoose web server. Tests embedded API.
|
||||
*/
|
||||
// Copyright (c) 2004-2009 Sergey Lyubka
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// Unit test for the mongoose web server. Tests embedded API.
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "mongoose.h"
|
||||
|
||||
#if !defined(LISTENING_PORT)
|
||||
#define LISTENING_PORT "23456"
|
||||
#endif /* !LISTENING_PORT */
|
||||
#endif
|
||||
|
||||
static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n\r\n";
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Connection: close\r\n\r\n";
|
||||
|
||||
static void
|
||||
test_get_var(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
char *value;
|
||||
static void test_get_var(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
char *var, *buf;
|
||||
size_t buf_len;
|
||||
const char *cl;
|
||||
int var_len;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
|
||||
value = mg_get_var(conn, "my_var");
|
||||
if (value != NULL) {
|
||||
mg_printf(conn, "Value: [%s]\n", value);
|
||||
mg_printf(conn, "Value size: [%u]\n", (unsigned) strlen(value));
|
||||
free(value);
|
||||
}
|
||||
buf_len = 0;
|
||||
var = buf = NULL;
|
||||
cl = mg_get_header(conn, "Content-Length");
|
||||
mg_printf(conn, "cl: %p\n", cl);
|
||||
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
|
||||
test_get_header(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
static void test_get_header(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
const char *value;
|
||||
int i;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
|
||||
{
|
||||
int i;
|
||||
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);
|
||||
}
|
||||
|
||||
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");
|
||||
if (value != NULL)
|
||||
if (value != NULL) {
|
||||
mg_printf(conn, "Value: [%s]", value);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_get_ri(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
static void test_get_request_info(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
int i;
|
||||
|
||||
mg_printf(conn, "%s", standard_reply);
|
||||
@ -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, "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",
|
||||
ri->http_headers[i].name,
|
||||
ri->http_headers[i].value);
|
||||
|
||||
}
|
||||
|
||||
mg_printf(conn, "Query string: [%s]\n",
|
||||
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 port: [%d]\n", ri->remote_port);
|
||||
mg_printf(conn, "Remote user: [%s]\n",
|
||||
ri->remote_user ? ri->remote_user : "");
|
||||
}
|
||||
|
||||
static void
|
||||
test_error(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
const char *value;
|
||||
|
||||
static void test_error(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
mg_printf(conn, "HTTP/1.1 %d XX\r\n"
|
||||
"Conntection: close\r\n\r\n", ri->status_code);
|
||||
mg_printf(conn, "Error: [%d]", ri->status_code);
|
||||
}
|
||||
|
||||
static void
|
||||
test_user_data(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
const char *value;
|
||||
static void test_post(struct mg_connection *conn,
|
||||
const struct mg_request_info *ri) {
|
||||
const char *cl;
|
||||
char *buf;
|
||||
int len;
|
||||
|
||||
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
|
||||
test_protect(struct mg_connection *conn, const struct mg_request_info *ri,
|
||||
void *user_data)
|
||||
{
|
||||
const char *allowed_user = * (char **) user_data;
|
||||
const char *remote_user = ri->remote_user;
|
||||
int allowed;
|
||||
static const struct test_config {
|
||||
enum mg_event event;
|
||||
const char *uri;
|
||||
void (*func)(struct mg_connection *, const struct mg_request_info *);
|
||||
} test_config[] = {
|
||||
{MG_NEW_REQUEST, "/test_get_header", &test_get_header},
|
||||
{MG_NEW_REQUEST, "/test_get_var", &test_get_var},
|
||||
{MG_NEW_REQUEST, "/test_get_request_info", &test_get_request_info},
|
||||
{MG_NEW_REQUEST, "/test_post", &test_post},
|
||||
{MG_HTTP_ERROR, "", &test_error},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
allowed = remote_user != NULL && !strcmp(allowed_user, remote_user);
|
||||
static void *callback(enum mg_event event,
|
||||
struct mg_connection *conn,
|
||||
struct mg_request_info *request_info) {
|
||||
int i;
|
||||
|
||||
* (long *) user_data = allowed ? 1 : 0;
|
||||
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
|
||||
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;
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", LISTENING_PORT, NULL};
|
||||
|
||||
ctx = mg_start();
|
||||
mg_set_option(ctx, "ports", LISTENING_PORT);
|
||||
|
||||
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();
|
||||
ctx = mg_start(callback, options);
|
||||
pause();
|
||||
return 0;
|
||||
}
|
||||
|
47
test/test.pl
47
test/test.pl
@ -146,7 +146,7 @@ if (scalar(@ARGV) > 0 and $ARGV[0] eq 'embedded') {
|
||||
}
|
||||
|
||||
# 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);
|
||||
my $saved_port = $port;
|
||||
$port = 12345;
|
||||
@ -156,11 +156,13 @@ unlink $config;
|
||||
kill_spawned_child();
|
||||
|
||||
# Spawn the server on port $port
|
||||
my $cmd = "$exe -ports $port -access_log access.log -error_log debug.log ".
|
||||
"-cgi_env CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||
"-mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||
"-root $root,/aiased=/etc/,/ta=$test_dir";
|
||||
$cmd .= ' -cgi_interp perl' if on_windows();
|
||||
my $cmd = "$exe -listening_ports $port -access_log_file access.log ".
|
||||
"-error_log_file debug.log ".
|
||||
"-cgi_environment CGI_FOO=foo,CGI_BAR=bar,CGI_BAZ=baz " .
|
||||
"-extra_mime_types .bar=foo/bar,.tar.gz=blah,.baz=foo " .
|
||||
'-put_delete_passwords_file test/passfile ' .
|
||||
"-document_root $root,/aiased=/etc/,/ta=$test_dir";
|
||||
$cmd .= ' -cgi_interpreter perl' if on_windows();
|
||||
spawn($cmd);
|
||||
|
||||
# Try to overflow: Send very long request
|
||||
@ -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");
|
||||
unlink $path;
|
||||
|
||||
kill_spawned_child();
|
||||
do_PUT_test();
|
||||
#do_embedded_test();
|
||||
kill_spawned_child();
|
||||
do_embedded_test();
|
||||
}
|
||||
|
||||
sub do_PUT_test {
|
||||
$cmd .= ' -auth_PUT test/passfile';
|
||||
spawn($cmd);
|
||||
|
||||
my $auth_header = "Authorization: Digest username=guest, ".
|
||||
"realm=mydomain.com, nonce=1145872809, uri=/put.txt, ".
|
||||
"response=896327350763836180c61d87578037d9, qop=auth, ".
|
||||
@ -379,16 +378,14 @@ sub do_PUT_test {
|
||||
o("PUT /put.txt HTTP/1.0\nExpect: 100-continue\nContent-Length: 4\n".
|
||||
"$auth_header\nabcd",
|
||||
"HTTP/1.1 100 Continue.+HTTP/1.1 200", 'PUT 100-Continue');
|
||||
kill_spawned_child();
|
||||
}
|
||||
|
||||
sub do_embedded_test {
|
||||
my $cmd = "cc -o $embed_exe $root/embed.c mongoose.c -I. ".
|
||||
"-DNO_SSL -lpthread -DLISTENING_PORT=\\\"$port\\\"";
|
||||
my $cmd = "cc -W -Wall -o $embed_exe $root/embed.c mongoose.c -I. ".
|
||||
"-pthread -DLISTENING_PORT=\\\"$port\\\"";
|
||||
if (on_windows()) {
|
||||
$cmd = "cl $root/embed.c mongoose.c /I. /nologo ".
|
||||
"/DNO_SSL /DLISTENING_PORT=\\\"$port\\\" ".
|
||||
"/link /out:$embed_exe.exe ws2_32.lib ";
|
||||
"/DLISTENING_PORT=\\\"$port\\\" /link /out:$embed_exe.exe ws2_32.lib ";
|
||||
}
|
||||
print $cmd, "\n";
|
||||
system($cmd) == 0 or fail("Cannot compile embedded unit test");
|
||||
@ -415,30 +412,24 @@ sub do_embedded_test {
|
||||
|
||||
# + in form data MUST be decoded to space
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 10\n\n".
|
||||
"my_var=b+c", 'Value: \[b c\]', 'mg_get_var 7', 0);
|
||||
"my_var=b+c", 'Value: \[b c\]', 'mg_get_var 9', 0);
|
||||
|
||||
# Test that big POSTed vars are not truncated
|
||||
my $my_var = 'x' x 64000;
|
||||
o("POST /test_get_var HTTP/1.0\nContent-Length: 64007\n\n".
|
||||
"my_var=$my_var", 'Value size: \[64000\]', 'mg_get_var 8', 0);
|
||||
|
||||
# Test PUT
|
||||
o("PUT /put HTTP/1.0\nContent-Length: 3\n\nabc",
|
||||
'\nabc$', 'put callback', 0);
|
||||
"my_var=$my_var", 'Value size: \[64000\]', 'mg_get_var 10', 0);
|
||||
|
||||
o("POST /test_get_request_info?xx=yy HTTP/1.0\nFoo: bar\n".
|
||||
"Content-Length: 3\n\na=b",
|
||||
'Method: \[POST\].URI: \[/test_get_request_info\].'.
|
||||
'HTTP version: \[1.0\].HTTP header \[Foo\]: \[bar\].'.
|
||||
'HTTP header \[Content-Length\]: \[3\].'.
|
||||
'Query string: \[xx=yy\].POST data: \[a=b\].'.
|
||||
'Query string: \[xx=yy\].'.
|
||||
'Remote IP: \[\d+\].Remote port: \[\d+\].'.
|
||||
'Remote user: \[\]'
|
||||
, 'request_info', 0);
|
||||
o("GET /not_exist HTTP/1.0\n\n", 'Error: \[404\]', '404 handler', 0);
|
||||
o("bad request\n\n", 'Error: \[400\]', '* error handler', 0);
|
||||
o("GET /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",
|
||||
# '401 Unauthorized', 'mg_protect_uri', 0);
|
||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=bill\n\n",
|
||||
@ -446,12 +437,6 @@ sub do_embedded_test {
|
||||
# o("GET /foo/secret HTTP/1.0\nAuthorization: Digest username=joe\n\n",
|
||||
# '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();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user