Procházet zdrojové kódy

API change for mg_start: most binary compatible across releases.

valenok před 15 roky
rodič
revize
546bec333c
5 změnil soubory, kde provedl 626 přidání a 670 odebrání
  1. 191 217
      main.c
  2. 219 205
      mongoose.c
  3. 86 79
      mongoose.h
  4. 114 138
      test/embed.c
  5. 16 31
      test/test.pl

+ 191 - 217
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;
-
-	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);
-
-	return (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;
+
+  options[1] = domain;
+  ctx = mg_start(NULL, options);
+  success = mg_modify_passwords_file(ctx, fname, user, pass);
+  mg_stop(ctx);
+
+  return success;
 }
 
-#define OFFSET(x) offsetof(struct mg_config, x)
-
-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}
-};
-
-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 show_usage_and_exit(void) {
+  const char **names;
+  int i, len;
+
+  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  ");
+
+  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");
+
+  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;
-		}
+static char *sdup(const char *str) {
+  char *p;
+  if ((p = malloc(strlen(str) + 1)) != NULL) {
+    strcpy(p, str);
+  }
+  return p;
+}
 
-	if (o->name == NULL)
-		show_usage_and_exit(config);
+static void set_option(char **options, const char *name, const char *value) {
+  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
-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;
-
-	/* 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);
-
-	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]);
-	}
+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;
+
+  /* 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();
+
+  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;
+    }
+  }
+
+  fp = fopen(config_file, "r");
+
+  /* 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);
+  }
+
+  /* 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_config	config;
-	struct mg_context	*ctx;
-
-	/* 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";
-
-	/* 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);
-	}
-
-	/* 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 */
+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);
-
-	/* Start Mongoose */
-	if ((ctx = mg_start(&config)) == NULL) {
-		(void) printf("%s\n", "Cannot initialize Mongoose context");
-		exit(EXIT_FAILURE);
-	}
-
-	(void) printf("Mongoose %s started on port(s) %s "
-	    "with web root [%s]\n",
-	    mg_version(), config.listening_ports, config.document_root);
-
-	fflush(stdout);
-	while (exit_flag == 0)
-		sleep(1);
-
-	(void) printf("Exiting on signal %d, "
-	    "waiting for all threads to finish...", exit_flag);
-	fflush(stdout);
-	mg_stop(ctx);
-	(void) printf("%s", " done.\n");
-
-	return (EXIT_SUCCESS);
+  signal(SIGTERM, signal_handler);
+  signal(SIGINT, signal_handler);
+
+  /* Start Mongoose */
+  ctx = mg_start(NULL, (const char **) options);
+  for (i = 0; options[i] != NULL; i++) {
+    free(options[i]);
+  }
+
+  if (ctx == NULL) {
+    (void) printf("%s\n", "Cannot initialize Mongoose context");
+    exit(EXIT_FAILURE);
+  }
+
+  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"));
+
+  fflush(stdout);
+  while (exit_flag == 0) {
+    sleep(1);
+  }
+
+  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;
 }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 219 - 205
mongoose.c


+ 86 - 79
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
-};
-
-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;
+// 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.
 };
 
-
-// Start the web server.
+// Prototype for the user-defined function. Mongoose calls this function
+// 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.
-// 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 *);
+// 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);
 
 

+ 114 - 138
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;
-
-	allowed = remote_user != NULL && !strcmp(allowed_user, remote_user);
-
-	* (long *) user_data = allowed ? 1 : 0;
+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}
+};
+
+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
-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;
 }

+ 16 - 31
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();
 }
 

Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů