Mongoose是一个简易的web服务器,所谓web服务器,简单的说就是把服务断的数据返回给客户端。
的源码很简单,主要就是Mongoose.c文件,里面包含了大部分的处理。
Mongoose里面有几个比较重要点的数据结构:
1、mg_context详解
mg_context结构体——表示Mongoose的上下文,也称为一个实例句柄。它的成员如下:
struct mg_context {
int stop_flag; /* Should we stop event loop */
SSL_CTX *ssl_ctx; /* SSL context */
FILE *access_log; /* Opened access log */
FILE *error_log; /* Opened error log */
struct socket listeners[MAX_LISTENING_SOCKETS];
int num_listeners;
struct callback callbacks[MAX_CALLBACKS];
int num_callbacks;
char *options[NUM_OPTIONS]; /* Configured opions */
pthread_mutex_t opt_mutex[NUM_OPTIONS]; /* Option protector */
int max_threads; /* Maximum number of threads */
int num_threads; /* Number of threads */
int num_idle; /* Number of idle threads */
pthread_mutex_t thr_mutex; /* Protects (max|num)_threads */
pthread_cond_t thr_cond;
pthread_mutex_t bind_mutex; /* Protects bind operations */
struct socket queue[20]; /* Accepted sockets */
int sq_head; /* Head of the socket queue */
int sq_tail; /* Tail of the socket queue */
pthread_cond_t empty_cond; /* Socket queue empty condvar */
pthread_cond_t full_cond; /* Socket queue full condvar */
mg_spcb_t ssl_password_callback;
mg_callback_t log_callback;
};
这个结构体在mg_start()中创建和初始化,其它函数大部分都会用它。因此mg_start()应该首先被调用。它非常重要,几乎所有的函数都要用到它。
1)、stop_flag表示是否应该停止的标记,它有三个可能的值0、1、2。 stop_flag=0表示 不应该停止,这是初始值;stop_flag=1表示停止,在mg_stop()函数中的一开始设置stop_flag=1,这会触发mg_fini(),且在mg_stop()中会一直等待mg_fini执行完成;stop_flag=2用于通知mg_stop(),mg_fini已经执行完成,stop_flag=2在mg_fini函数中的末尾设置。
2)、ssl_ctx是结构体ssl_ctx_st的实例,它来自OpenSSL开源项目,作者把它放到这里的原因是使其独立于OpenSSL的源码安装,这样只有系统上面安装有SSL库,mongoose+SSL就能编译通过。
3)、access_log、error_log很明显是指向访问日志文件、错误日志文件。
4)、listeners数组存储mongoose建立的多个web server,每个web server都是listeners数组中的一个元素。例如,一个服务器可以分别在端口8080、8888建立web server,这样8080端口的那个server是listerns数组中的一个元素,8888端口的那个server也是listeners数组中的一个元素。换句话说,listeners数组表示web server的socket地址。num_listeners表示listeners数组的元素个数。
5)、callbacks是结构体callback的数组,而callback本身是一个结构体,包含几个回调句柄。num_callbacks是callbacks数组元素的个数。
6)、options数组,是用于存储配置选项的,例如端口号、工作目录等等。opt_mutext对配置进行操作的互斥变量。
7)、max_threads表示允许的最大线程数量、num_threads表示当前的线程数量、num_idle表示空闲的线程数量。之所以会有空闲进程,是因为当创建一个线程处理连接请求之后,它会保持一段时间空闲而不是直接销毁。如果这里再用新的连接到来或等待队列中有需要处理的连接,空闲进程会被分配去处理。
8)、thr_mutex、thr_cond、bind_mutex是用于互斥信号量和条件变量。
9)、queue[20]队列数组存储client的连接请求,每个元素都是client的socket。sq_head、sq_tail分别是队列头、尾用于操作队列queue。empty_cond、full_cond分别表示队列是否为空、满的条件变量。
10)、ssl_password_callback和log_callback都是函数指针,分别指向SSL密码处理函数、log处理函数。他们原型是:
/*
* Register SSL password handler.
* This is needed only if SSL certificate asks for a password. Instead of
* prompting for a password on a console a specified function will be called.
*/
typedef int (*mg_spcb_t)(char *buf, int num, int w, void *key);
/*
* User-defined callback function prototype for URI handling, error handling,
* or logging server messages.
*/
typedef void (*mg_callback_t)(struct mg_connection *,
const struct mg_request_info *info, void *user_data
下面看下如何使用它。
首先我们需要启动http服务,这是通过调用mg_start来实现的
struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
const char **options) {
struct mg_context *ctx;
const char *name, *value, *default_value;
int i;
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 1111");
#if defined(_WIN32) && !defined(__SYMBIAN32__)
WSADATA data;
WSAStartup(MAKEWORD(2,2), &data);
#endif // _WIN32
// Allocate context and initialize reasonable general case defaults.
// TODO(lsm): do proper error handling here.
ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
ctx->user_callback = user_callback; //保存回调
ctx->user_data = user_data;
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 2222");
while (options && (name = *options++) != NULL) { //解析option
if ((i = get_option_index(name)) == -1) {
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "Invalid option: %s", name);
free_context(ctx);
return NULL;
} else if ((value = *options++) == NULL) {
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "%s: option value cannot be NULL", name);
free_context(ctx);
return NULL;
}
ctx->config[i] = mg_strdup(value);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "config:[%s] -> [%s]", name, value);
}
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 3333");
// Set default value if needed
for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
if (ctx->config[i] == NULL && default_value != NULL) {
ctx->config[i] = mg_strdup(default_value);
DEBUG_TRACE(("Setting default: [%s] -> [%s]",
config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
default_value));
}
}
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 4444");
// NOTE(lsm): order is important here. SSL certificates must
// be initialized before listening ports. UID must be set last.
int a = !set_gpass_option(ctx);
int b = !set_ssl_option(ctx);
int c = !set_ports_option(ctx);//设置监听端口
int d = !set_uid_option(ctx);
int e = !set_acl_option(ctx);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set_gpass_option(ctx):%d",a);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set_ports_option(ctx):%d",b);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set_acl_option(ctx):%d",c);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set_uid_option(ctx):%d",d);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set_ssl_option(ctx):%d",e);
if (a || b || c || d || e) {
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start set.......");
free_context(ctx);
return NULL;
}
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 5555");
#if !defined(_WIN32) && !defined(__SYMBIAN32__)
// Ignore SIGPIPE signal, so if browser cancels the request, it
// won't kill the whole process.
(void) signal(SIGPIPE, SIG_IGN);
#endif // !_WIN32
(void) pthread_mutex_init(&ctx->mutex, NULL);
(void) pthread_cond_init(&ctx->cond, NULL);
(void) pthread_cond_init(&ctx->sq_empty, NULL);
(void) pthread_cond_init(&ctx->sq_full, NULL);
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 6666");
// Start master (listening) thread
start_thread(ctx, (mg_thread_func_t) master_thread, ctx);//启动主线程,处理到来的连接
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 7777");
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "atoi(ctx->config[NUM_THREADS]) = %d.", atoi(ctx->config[NUM_THREADS]));
// Start worker threads
for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) != 0) {//启动工作线程,具体连接处理
cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
} else {
ctx->num_threads++;
}
}
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "hp mg_start 8888");
return ctx;
}
mg_start主要是进行一些初始化操作,然后等待启动一个线程等待客户端连接的到来。再启动了一定数量的工作线程进行具体的处理
首先来看一下主线程:
再来年下worker_thread
这里面的读取请求是通过read_request,里面再调用pull进行数据的读取
// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
// a directory, or call embedded function, etcetera.
static void handle_request(struct mg_connection *conn) {
struct mg_request_info *ri = &conn->request_info;
char path[PATH_MAX];
int uri_len;
struct mgstat st;
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "handle_request");
if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
*conn->request_info.query_string++ = '\0';
}
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "ri->uri = %s.", ri->uri);
uri_len = strlen(ri->uri);
(void) url_decode(ri->uri, (size_t) uri_len, ri->uri,
(size_t) (uri_len + 1), 0);
remove_double_dots_and_double_slashes(ri->uri);
convert_uri_to_file_name(conn, ri->uri, path, sizeof(path));
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "ri->request_method = %s.", ri->request_method);
DEBUG_TRACE(("%s", ri->uri));
if (!check_authorization(conn, path)) {
send_authorization_request(conn);
} else if (call_user(conn, MG_NEW_REQUEST) != NULL) {//这里调用用户注册的函数
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "call_user .MG_NEW_REQUEST");
// Do nothing, callback has served the request
} else if (strstr(path, PASSWORDS_FILE_NAME)) {
// Do not allow to view passwords files
send_http_error(conn, 403, "Forbidden", "Access Forbidden");
} else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
send_http_error(conn, 404, "Not Found", "Not Found");
} else if ((!strcmp(ri->request_method, "PUT") || !strcmp(
ri->request_method, "DELETE"))
&& (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL
|| !is_authorized_for_put(conn))) {
send_authorization_request(conn);
} else if (!strcmp(ri->request_method, "PUT")) {
put_file(conn, path);
} else if (!strcmp(ri->request_method, "DELETE")) {
if (mg_remove(path) == 0) {
send_http_error(conn, 200, "OK", "");
} else {
send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
strerror(ERRNO));
}
} else if (mg_stat(path, &st) != 0) {
send_http_error(conn, 404, "Not Found", "%s", "File not found");
} else if (st.is_directory && ri->uri[uri_len - 1] != '/') {
(void) mg_printf(conn, "HTTP/1.1 301 Moved Permanently\r\n"
"Location: %s/\r\n\r\n", ri->uri);
} else if (st.is_directory && !substitute_index_file(conn, path,
sizeof(path), &st)) {
if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
handle_directory_request(conn, path);
} else {
send_http_error(conn, 403, "Directory Listing Denied",
"Directory listing denied");
}
} else if (match_extension(path, conn->ctx->config[CGI_EXTENSIONS])) {
if (strcmp(ri->request_method, "POST") && strcmp(ri->request_method,
"GET")) {
send_http_error(conn, 501, "Not Implemented",
"Method %s is not implemented", ri->request_method);
} else {
handle_cgi_request(conn, path);
}
} else if (match_extension(path, conn->ctx->config[SSI_EXTENSIONS])) {
handle_ssi_file_request(conn, path);
} else if (is_not_modified(conn, &st)) {
send_http_error(conn, 304, "Not Modified", "");
} else {
__android_log_print(ANDROID_LOG_DEBUG, "mongoose", "handle_file_request");
handle_file_request(conn, path, &st);
}
}
再来看一下请求解析的函数:
static int parse_http_request(char *buf, struct mg_request_info *ri) {
int status = 0;
// RFC says that all initial whitespaces should be ingored
while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
buf++;
}
ri->request_method = skip(&buf, " ");
ri->uri = skip(&buf, " ");
ri->http_version = skip(&buf, "\r\n");
if (is_valid_http_method(ri->request_method) && strncmp(ri->http_version,
"HTTP/", 5) == 0) {
ri->http_version += 5; /* Skip "HTTP/" */
parse_http_headers(&buf, ri);
status = 1;
}
return status;
}
它的主要工作就是从buf中提取出信息放到ri(一个mg_request_info结构)中去,因为buf是一个无结构的字符串数组。要将它存储到ri中去,需要找到对应的子串。
这里主要用到了skip()、parse_http_headers()方法,其中skip()很关键
当我们要发送数据给client端时,可以通过mg_write函数来实现,这个函数可以在回调函数里面去调用。
mongoose的基本流程大概也就这样,其它的就以后需要时再去具体分析吧。
转自: http://blog.youkuaiyun.com/new_abc/article/details/7679661