From 6cf296515b0ee365f2c523ba40462d5d714868e7 Mon Sep 17 00:00:00 2001 From: valenok Date: Sun, 29 Aug 2010 23:19:53 +0100 Subject: [PATCH] added debug trace in mg_start. restored stderr duping in spawn_process() --- bindings/python/example.py | 75 ++++++------ bindings/python/mongoose.py | 230 ++++++++++++++---------------------- mongoose.c | 3 +- 3 files changed, 127 insertions(+), 181 deletions(-) diff --git a/bindings/python/example.py b/bindings/python/example.py index ecd26967..ad58d64f 100644 --- a/bindings/python/example.py +++ b/bindings/python/example.py @@ -8,50 +8,51 @@ import mongoose import sys # Handle /show and /form URIs. -def uri_handler(conn, info): - if info.uri == '/show': - conn.printf('%s', 'HTTP/1.0 200 OK\r\n') - conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') - conn.printf('%s %s\n', info.request_method, info.uri) - if info.request_method == 'POST': - content_len = conn.get_header('Content-Length') - post_data = conn.read(int(content_len)) - my_var = conn.get_var(post_data, 'my_var') - else: - my_var = conn.get_qsvar(info, 'my_var') - conn.printf('my_var: %s\n', my_var or '') - conn.printf('HEADERS: \n') - for header in info.http_headers[:info.num_headers]: - conn.printf(' %s: %s\n', header.name, header.value) - return mongoose.MG_SUCCESS - elif info.uri == '/form': - conn.write('HTTP/1.0 200 OK\r\n' - 'Content-Type: text/html\r\n\r\n' - 'Use GET: link' - '
' - 'Use POST: type text and submit: ' - '' - '' - '
') - return mongoose.MG_SUCCESS - else: - return mongoose.MG_ERROR +def EventHandler(conn, info): + if info.uri == '/show': + conn.printf('%s', 'HTTP/1.0 200 OK\r\n') + conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') + conn.printf('%s %s\n', info.request_method, info.uri) + if info.request_method == 'POST': + content_len = conn.get_header('Content-Length') + post_data = conn.read(int(content_len)) + my_var = conn.get_var(post_data, 'my_var') + else: + my_var = conn.get_qsvar(info, 'my_var') + conn.printf('my_var: %s\n', my_var or '') + conn.printf('HEADERS: \n') + for header in info.http_headers[:info.num_headers]: + conn.printf(' %s: %s\n', header.name, header.value) + return mongoose.MG_SUCCESS + elif info.uri == '/form': + conn.write('HTTP/1.0 200 OK\r\n' + 'Content-Type: text/html\r\n\r\n' + 'Use GET: link' + '
' + 'Use POST: type text and submit: ' + '' + '' + '
') + return mongoose.MG_SUCCESS + else: + return mongoose.MG_ERROR # Invoked each time HTTP error is triggered. def error_handler(conn, info): - conn.printf('%s', 'HTTP/1.0 200 OK\r\n') - conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') - conn.printf('HTTP error: %d\n', info.status_code) - return mongoose.MG_SUCCESS + conn.printf('%s', 'HTTP/1.0 200 OK\r\n') + conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') + conn.printf('HTTP error: %d\n', info.status_code) + return mongoose.MG_SUCCESS # Create mongoose object, and register '/foo' URI handler # List of options may be specified in the contructor -server = mongoose.Mongoose(document_root='/tmp', - new_request_handler=uri_handler, - http_error_handler=error_handler, - listening_ports='8080') +server = mongoose.Mongoose(EventHandler, + document_root='/tmp', + listening_ports='8080') + +print ('Mongoose started on port %s, press enter to quit' + % server.get_option('listening_ports')) -print 'Starting Mongoose server, press enter to quit' sys.stdin.read(1) # Deleting server object stops all serving threads diff --git a/bindings/python/mongoose.py b/bindings/python/mongoose.py index b8f3cfd7..d46e20a2 100644 --- a/bindings/python/mongoose.py +++ b/bindings/python/mongoose.py @@ -26,14 +26,10 @@ This module provides python binding for the Mongoose web server. There are two classes defined: Connection: - wraps all functions that accept struct mg_connection pointer - as first argument + as first argument. Mongoose: wraps all functions that accept struct mg_context pointer as - first argument. All valid option names, settable via mg_set_option(), - are settable/gettable as the attributes of the Mongoose object. - In addition to those, two attributes are available: - 'version': string, contains server version - 'options': array of all known options. + first argument. Creating Mongoose object automatically starts server, deleting object automatically stops it. There is no need to call mg_start() or mg_stop(). @@ -44,165 +40,113 @@ import ctypes import os -MG_ERROR = 0 -MG_SUCCESS = 1 -MG_NOT_FOUND = 2 -MG_BUFFER_TOO_SMALL = 3 - - class mg_header(ctypes.Structure): - """A wrapper for struct mg_header.""" - _fields_ = [ - ('name', ctypes.c_char_p), - ('value', ctypes.c_char_p), - ] + """A wrapper for struct mg_header.""" + _fields_ = [ + ('name', ctypes.c_char_p), + ('value', ctypes.c_char_p), + ] class mg_request_info(ctypes.Structure): - """A wrapper for struct mg_request_info.""" - _fields_ = [ - ('request_method', ctypes.c_char_p), - ('uri', ctypes.c_char_p), - ('http_version', ctypes.c_char_p), - ('query_string', ctypes.c_char_p), - ('remote_user', ctypes.c_char_p), - ('log_message', ctypes.c_char_p), - ('remote_ip', ctypes.c_long), - ('remote_port', ctypes.c_int), - ('status_code', ctypes.c_int), - ('is_ssl', ctypes.c_int), - ('num_headers', ctypes.c_int), - ('http_headers', mg_header * 64), - ] + """A wrapper for struct mg_request_info.""" + _fields_ = [ + ('request_method', ctypes.c_char_p), + ('uri', ctypes.c_char_p), + ('http_version', ctypes.c_char_p), + ('query_string', ctypes.c_char_p), + ('remote_user', ctypes.c_char_p), + ('log_message', ctypes.c_char_p), + ('remote_ip', ctypes.c_long), + ('remote_port', ctypes.c_int), + ('status_code', ctypes.c_int), + ('is_ssl', ctypes.c_int), + ('num_headers', ctypes.c_int), + ('http_headers', mg_header * 64), + ] -mg_callback_t = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_voidp, - ctypes.POINTER(mg_request_info)) - - -class mg_config(ctypes.Structure): - """A wrapper for struct mg_config.""" - _fields_ = [ - ('document_root', ctypes.c_char_p), - ('index_files', ctypes.c_char_p), - ('ssl_certificate', ctypes.c_char_p), - ('listening_ports', ctypes.c_char_p), - ('cgi_extensions', ctypes.c_char_p), - ('cgi_interpreter', ctypes.c_char_p), - ('cgi_environment', ctypes.c_char_p), - ('ssi_extensions', ctypes.c_char_p), - ('auth_domain', ctypes.c_char_p), - ('protect', ctypes.c_char_p), - ('global_passwords_file', ctypes.c_char_p), - ('put_delete_passwords_file', ctypes.c_char_p), - ('access_log_file', ctypes.c_char_p), - ('error_log_file', ctypes.c_char_p), - ('acl', ctypes.c_char_p), - ('uid', ctypes.c_char_p), - ('mime_types', ctypes.c_char_p), - ('enable_directory_listing', ctypes.c_char_p), - ('num_threads', ctypes.c_char_p), - ('new_request_handler', mg_callback_t), - ('http_error_handler', mg_callback_t), - ('event_log_handler', mg_callback_t), - ('ssl_password_handler', mg_callback_t), - ] +mg_callback_t = ctypes.CFUNCTYPE(ctypes.c_void_p, + ctypes.c_void_p, + ctypes.POINTER(mg_request_info)) class Connection(object): - """A wrapper class for all functions that take - struct mg_connection * as the first argument.""" + """A wrapper class for all functions that take + struct mg_connection * as the first argument.""" - def __init__(self, mongoose, connection): - self.m = mongoose - self.conn = ctypes.c_voidp(connection) + def __init__(self, mongoose, connection): + self.m = mongoose + self.conn = ctypes.c_void_p(connection) - def get_header(self, name): - val = self.m.dll.mg_get_header(self.conn, name) - return ctypes.c_char_p(val).value + def get_header(self, name): + val = self.m.dll.mg_get_header(self.conn, name) + return ctypes.c_char_p(val).value - def get_var(self, data, name): - size = len(data) - buf = ctypes.create_string_buffer(size) - n = self.m.dll.mg_get_var(data, size, name, buf, size) - return n == MG_SUCCESS and buf or None - - def get_qsvar(self, request_info, name): - qs = request_info.query_string - return qs and self.get_var(qs, name) or None + def get_var(self, data, name): + size = len(data) + buf = ctypes.create_string_buffer(size) + n = self.m.dll.mg_get_var(data, size, name, buf, size) + return n == MG_SUCCESS and buf or None + + def printf(self, fmt, *args): + val = self.m.dll.mg_printf(self.conn, fmt, *args) + return ctypes.c_int(val).value - def printf(self, fmt, *args): - val = self.m.dll.mg_printf(self.conn, fmt, *args) - return ctypes.c_int(val).value + def write(self, data): + val = self.m.dll.mg_write(self.conn, data, len(data)) + return ctypes.c_int(val).value - def write(self, data): - val = self.m.dll.mg_write(self.conn, data, len(data)) - return ctypes.c_int(val).value - - def read(self, size): - buf = ctypes.create_string_buffer(size) - n = self.m.dll.mg_read(self.conn, buf, size) - print size, buf, n - return n <= 0 and None or buf[:n] + def read(self, size): + buf = ctypes.create_string_buffer(size) + n = self.m.dll.mg_read(self.conn, buf, size) + print size, buf, n + return n <= 0 and None or buf[:n] class Mongoose(object): - """A wrapper class for Mongoose shared library.""" + """A wrapper class for Mongoose shared library.""" - def __init__(self, **kwargs): - dll_extension = os.name == 'nt' and 'dll' or 'so' - self.dll = ctypes.CDLL('_mongoose.%s' % dll_extension) + def __init__(self, callback, **kwargs): + dll_extension = os.name == 'nt' and 'dll' or 'so' + self.dll = ctypes.CDLL('_mongoose.%s' % dll_extension) - self.dll.mg_start.restype = ctypes.c_void_p - self.dll.mg_modify_passwords_file.restype = ctypes.c_int - self.dll.mg_read.restype = ctypes.c_int - self.dll.mg_write.restype = ctypes.c_int - self.dll.mg_printf.restype = ctypes.c_int - self.dll.mg_get_header.restype = ctypes.c_char_p - self.dll.mg_get_var.restype = ctypes.c_int - self.dll.mg_get_qsvar.restype = ctypes.c_int - self.dll.mg_get_cookie.restype = ctypes.c_int + self.dll.mg_start.restype = ctypes.c_void_p + self.dll.mg_modify_passwords_file.restype = ctypes.c_int + self.dll.mg_read.restype = ctypes.c_int + self.dll.mg_write.restype = ctypes.c_int + self.dll.mg_printf.restype = ctypes.c_int + self.dll.mg_get_header.restype = ctypes.c_char_p + self.dll.mg_get_var.restype = ctypes.c_int + self.dll.mg_get_cookie.restype = ctypes.c_int + self.dll.mg_get_option.restype = ctypes.c_char_p - self.callbacks = [] - self.config = mg_config(num_threads='5', - enable_directory_listing='yes', - listening_ports='8080', - document_root='.', - auth_domain='mydomain.com') - for key, value in kwargs.iteritems(): - if key in ('new_request_handler', - 'http_error_handler', - 'event_log_handler', - 'ssl_password_handler'): - cb = self.MakeHandler(value) - setattr(self.config, key, cb) - else: - setattr(self.config, key, str(value)) - self.ctx = self.dll.mg_start(ctypes.byref(self.config)) + if callback: + # Create a closure that will be called by the shared library. + def func(connection, request_info): + # Wrap connection pointer into the connection + # object and call Python callback + conn = Connection(self, connection) + if python_func(conn, request_info.contents): + return 'non-null-pointer' + else: + return ctypes.c_void_p(0) - def __del__(self): - """Destructor, stop Mongoose instance.""" - self.dll.mg_stop(ctypes.c_void_p(self.ctx)) + # Convert the closure into C callable object + self.callback = mg_callback_t(func) + self.callback.restype = ctypes.c_void_p + else: + self.callback = ctypes.c_void_p(0) - def MakeHandler(self, python_func): - """Return C callback from given Python callback.""" + args = [y for x in kwargs.items() for y in x] + [None] + options = (ctypes.c_char_p * len(args))(*args) - # Create a closure that will be called by the shared library. - def func(connection, request_info): - # Wrap connection pointer into the connection - # object and call Python callback - conn = Connection(self, connection) - status = python_func(conn, request_info.contents) - return status == MG_SUCCESS and MG_SUCCESS or MG_ERROR +# self.ctx = self.dll.mg_start(self.callback, options) + self.ctx = self.dll.mg_start(ctypes.c_void_p(0), options) - # Convert the closure into C callable object - c_func = mg_callback_t(func) - c_func.restype = ctypes.c_int + def __del__(self): + """Destructor, stop Mongoose instance.""" + self.dll.mg_stop(ctypes.c_void_p(self.ctx)) - # Store created callback in the list, so it is kept alive - # during context lifetime. Otherwise, python can garbage - # collect it, and C code will crash trying to call it. - self.callbacks.append(c_func) - - return c_func + def get_option(self, name): + return self.dll.mg_get_option(self.ctx, name) diff --git a/mongoose.c b/mongoose.c index c501c656..6f7cc00b 100644 --- a/mongoose.c +++ b/mongoose.c @@ -1176,7 +1176,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, } else if (dup2(fd_stdout, 1) == -1) { cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO)); } else { - //(void) dup2(fd_stdout, 2); + (void) dup2(fd_stdout, 2); (void) close(fd_stdin); (void) close(fd_stdout); @@ -3827,6 +3827,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) { free(ctx->config[i]); } ctx->config[i] = mg_strdup(value); + DEBUG_TRACE(("[%s] -> [%s]", name, value)); } if (!verify_document_root(ctx)) {