Commit 6cf29651 authored by valenok's avatar valenok

added debug trace in mg_start. restored stderr duping in spawn_process()

parent 546bec33
...@@ -8,50 +8,51 @@ import mongoose ...@@ -8,50 +8,51 @@ import mongoose
import sys import sys
# Handle /show and /form URIs. # Handle /show and /form URIs.
def uri_handler(conn, info): def EventHandler(conn, info):
if info.uri == '/show': if info.uri == '/show':
conn.printf('%s', 'HTTP/1.0 200 OK\r\n') conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
conn.printf('%s %s\n', info.request_method, info.uri) conn.printf('%s %s\n', info.request_method, info.uri)
if info.request_method == 'POST': if info.request_method == 'POST':
content_len = conn.get_header('Content-Length') content_len = conn.get_header('Content-Length')
post_data = conn.read(int(content_len)) post_data = conn.read(int(content_len))
my_var = conn.get_var(post_data, 'my_var') my_var = conn.get_var(post_data, 'my_var')
else: else:
my_var = conn.get_qsvar(info, 'my_var') my_var = conn.get_qsvar(info, 'my_var')
conn.printf('my_var: %s\n', my_var or '<not set>') conn.printf('my_var: %s\n', my_var or '<not set>')
conn.printf('HEADERS: \n') conn.printf('HEADERS: \n')
for header in info.http_headers[:info.num_headers]: for header in info.http_headers[:info.num_headers]:
conn.printf(' %s: %s\n', header.name, header.value) conn.printf(' %s: %s\n', header.name, header.value)
return mongoose.MG_SUCCESS return mongoose.MG_SUCCESS
elif info.uri == '/form': elif info.uri == '/form':
conn.write('HTTP/1.0 200 OK\r\n' conn.write('HTTP/1.0 200 OK\r\n'
'Content-Type: text/html\r\n\r\n' 'Content-Type: text/html\r\n\r\n'
'Use GET: <a href="/show?my_var=hello">link</a>' 'Use GET: <a href="/show?my_var=hello">link</a>'
'<form action="/show" method="POST">' '<form action="/show" method="POST">'
'Use POST: type text and submit: ' 'Use POST: type text and submit: '
'<input type="text" name="my_var"/>' '<input type="text" name="my_var"/>'
'<input type="submit"/>' '<input type="submit"/>'
'</form>') '</form>')
return mongoose.MG_SUCCESS return mongoose.MG_SUCCESS
else: else:
return mongoose.MG_ERROR return mongoose.MG_ERROR
# Invoked each time HTTP error is triggered. # Invoked each time HTTP error is triggered.
def error_handler(conn, info): def error_handler(conn, info):
conn.printf('%s', 'HTTP/1.0 200 OK\r\n') conn.printf('%s', 'HTTP/1.0 200 OK\r\n')
conn.printf('%s', 'Content-Type: text/plain\r\n\r\n') conn.printf('%s', 'Content-Type: text/plain\r\n\r\n')
conn.printf('HTTP error: %d\n', info.status_code) conn.printf('HTTP error: %d\n', info.status_code)
return mongoose.MG_SUCCESS return mongoose.MG_SUCCESS
# Create mongoose object, and register '/foo' URI handler # Create mongoose object, and register '/foo' URI handler
# List of options may be specified in the contructor # List of options may be specified in the contructor
server = mongoose.Mongoose(document_root='/tmp', server = mongoose.Mongoose(EventHandler,
new_request_handler=uri_handler, document_root='/tmp',
http_error_handler=error_handler, listening_ports='8080')
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) sys.stdin.read(1)
# Deleting server object stops all serving threads # Deleting server object stops all serving threads
......
...@@ -26,14 +26,10 @@ This module provides python binding for the Mongoose web server. ...@@ -26,14 +26,10 @@ This module provides python binding for the Mongoose web server.
There are two classes defined: There are two classes defined:
Connection: - wraps all functions that accept struct mg_connection pointer 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 Mongoose: wraps all functions that accept struct mg_context pointer as
first argument. All valid option names, settable via mg_set_option(), first argument.
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.
Creating Mongoose object automatically starts server, deleting object Creating Mongoose object automatically starts server, deleting object
automatically stops it. There is no need to call mg_start() or mg_stop(). automatically stops it. There is no need to call mg_start() or mg_stop().
...@@ -44,165 +40,113 @@ import ctypes ...@@ -44,165 +40,113 @@ import ctypes
import os import os
MG_ERROR = 0
MG_SUCCESS = 1
MG_NOT_FOUND = 2
MG_BUFFER_TOO_SMALL = 3
class mg_header(ctypes.Structure): class mg_header(ctypes.Structure):
"""A wrapper for struct mg_header.""" """A wrapper for struct mg_header."""
_fields_ = [ _fields_ = [
('name', ctypes.c_char_p), ('name', ctypes.c_char_p),
('value', ctypes.c_char_p), ('value', ctypes.c_char_p),
] ]
class mg_request_info(ctypes.Structure): class mg_request_info(ctypes.Structure):
"""A wrapper for struct mg_request_info.""" """A wrapper for struct mg_request_info."""
_fields_ = [ _fields_ = [
('request_method', ctypes.c_char_p), ('request_method', ctypes.c_char_p),
('uri', ctypes.c_char_p), ('uri', ctypes.c_char_p),
('http_version', ctypes.c_char_p), ('http_version', ctypes.c_char_p),
('query_string', ctypes.c_char_p), ('query_string', ctypes.c_char_p),
('remote_user', ctypes.c_char_p), ('remote_user', ctypes.c_char_p),
('log_message', ctypes.c_char_p), ('log_message', ctypes.c_char_p),
('remote_ip', ctypes.c_long), ('remote_ip', ctypes.c_long),
('remote_port', ctypes.c_int), ('remote_port', ctypes.c_int),
('status_code', ctypes.c_int), ('status_code', ctypes.c_int),
('is_ssl', ctypes.c_int), ('is_ssl', ctypes.c_int),
('num_headers', ctypes.c_int), ('num_headers', ctypes.c_int),
('http_headers', mg_header * 64), ('http_headers', mg_header * 64),
] ]
mg_callback_t = ctypes.CFUNCTYPE(ctypes.c_int, mg_callback_t = ctypes.CFUNCTYPE(ctypes.c_void_p,
ctypes.c_voidp, ctypes.c_void_p,
ctypes.POINTER(mg_request_info)) 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),
]
class Connection(object): class Connection(object):
"""A wrapper class for all functions that take """A wrapper class for all functions that take
struct mg_connection * as the first argument.""" struct mg_connection * as the first argument."""
def __init__(self, mongoose, connection): def __init__(self, mongoose, connection):
self.m = mongoose self.m = mongoose
self.conn = ctypes.c_voidp(connection) self.conn = ctypes.c_void_p(connection)
def get_header(self, name): def get_header(self, name):
val = self.m.dll.mg_get_header(self.conn, name) val = self.m.dll.mg_get_header(self.conn, name)
return ctypes.c_char_p(val).value return ctypes.c_char_p(val).value
def get_var(self, data, name): def get_var(self, data, name):
size = len(data) size = len(data)
buf = ctypes.create_string_buffer(size) buf = ctypes.create_string_buffer(size)
n = self.m.dll.mg_get_var(data, size, name, buf, size) n = self.m.dll.mg_get_var(data, size, name, buf, size)
return n == MG_SUCCESS and buf or None return n == MG_SUCCESS and buf or None
def get_qsvar(self, request_info, name): def printf(self, fmt, *args):
qs = request_info.query_string val = self.m.dll.mg_printf(self.conn, fmt, *args)
return qs and self.get_var(qs, name) or None return ctypes.c_int(val).value
def printf(self, fmt, *args): def write(self, data):
val = self.m.dll.mg_printf(self.conn, fmt, *args) val = self.m.dll.mg_write(self.conn, data, len(data))
return ctypes.c_int(val).value return ctypes.c_int(val).value
def write(self, data): def read(self, size):
val = self.m.dll.mg_write(self.conn, data, len(data)) buf = ctypes.create_string_buffer(size)
return ctypes.c_int(val).value n = self.m.dll.mg_read(self.conn, buf, size)
print size, buf, n
def read(self, size): return n <= 0 and None or buf[:n]
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): class Mongoose(object):
"""A wrapper class for Mongoose shared library.""" """A wrapper class for Mongoose shared library."""
def __init__(self, **kwargs): def __init__(self, callback, **kwargs):
dll_extension = os.name == 'nt' and 'dll' or 'so' dll_extension = os.name == 'nt' and 'dll' or 'so'
self.dll = ctypes.CDLL('_mongoose.%s' % dll_extension) self.dll = ctypes.CDLL('_mongoose.%s' % dll_extension)
self.dll.mg_start.restype = ctypes.c_void_p self.dll.mg_start.restype = ctypes.c_void_p
self.dll.mg_modify_passwords_file.restype = ctypes.c_int self.dll.mg_modify_passwords_file.restype = ctypes.c_int
self.dll.mg_read.restype = ctypes.c_int self.dll.mg_read.restype = ctypes.c_int
self.dll.mg_write.restype = ctypes.c_int self.dll.mg_write.restype = ctypes.c_int
self.dll.mg_printf.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_header.restype = ctypes.c_char_p
self.dll.mg_get_var.restype = ctypes.c_int 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_get_cookie.restype = ctypes.c_int self.dll.mg_get_option.restype = ctypes.c_char_p
self.callbacks = [] if callback:
self.config = mg_config(num_threads='5', # Create a closure that will be called by the shared library.
enable_directory_listing='yes', def func(connection, request_info):
listening_ports='8080', # Wrap connection pointer into the connection
document_root='.', # object and call Python callback
auth_domain='mydomain.com') conn = Connection(self, connection)
for key, value in kwargs.iteritems(): if python_func(conn, request_info.contents):
if key in ('new_request_handler', return 'non-null-pointer'
'http_error_handler', else:
'event_log_handler', return ctypes.c_void_p(0)
'ssl_password_handler'):
cb = self.MakeHandler(value) # Convert the closure into C callable object
setattr(self.config, key, cb) self.callback = mg_callback_t(func)
else: self.callback.restype = ctypes.c_void_p
setattr(self.config, key, str(value)) else:
self.ctx = self.dll.mg_start(ctypes.byref(self.config)) self.callback = ctypes.c_void_p(0)
def __del__(self): args = [y for x in kwargs.items() for y in x] + [None]
"""Destructor, stop Mongoose instance.""" options = (ctypes.c_char_p * len(args))(*args)
self.dll.mg_stop(ctypes.c_void_p(self.ctx))
# self.ctx = self.dll.mg_start(self.callback, options)
def MakeHandler(self, python_func): self.ctx = self.dll.mg_start(ctypes.c_void_p(0), options)
"""Return C callback from given Python callback."""
def __del__(self):
# Create a closure that will be called by the shared library. """Destructor, stop Mongoose instance."""
def func(connection, request_info): self.dll.mg_stop(ctypes.c_void_p(self.ctx))
# Wrap connection pointer into the connection
# object and call Python callback def get_option(self, name):
conn = Connection(self, connection) return self.dll.mg_get_option(self.ctx, name)
status = python_func(conn, request_info.contents)
return status == MG_SUCCESS and MG_SUCCESS or MG_ERROR
# Convert the closure into C callable object
c_func = mg_callback_t(func)
c_func.restype = ctypes.c_int
# 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
...@@ -1176,7 +1176,7 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, ...@@ -1176,7 +1176,7 @@ 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);
...@@ -3827,6 +3827,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) { ...@@ -3827,6 +3827,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, const char **options) {
free(ctx->config[i]); free(ctx->config[i]);
} }
ctx->config[i] = mg_strdup(value); ctx->config[i] = mg_strdup(value);
DEBUG_TRACE(("[%s] -> [%s]", name, value));
} }
if (!verify_document_root(ctx)) { if (!verify_document_root(ctx)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment