GNU Libmicrohttpd是一个用来在项目中内嵌http服务器的C语言库。这是一款免费软件,并且是GNU项目的一部分。 它具有以下几个鲜明的特点:
- C语言库,小而快
- 简易的API,且都是可重入的
- 支持 HTTP 1.1 可以同时侦听多个端口
- 四种不同的线程模式(select、poll、pthread、thread pool)
- 库平台支持 GNU/Linux, FreeBSD, OpenBSD, NetBSD, Android, Darwin (macOS), W32, OpenIndiana/Solaris, z/OS 等
- 支持IPv6、SHOUTcast等
- 生成的二制文件只有32K(不包含TLS/SSL等额外功能)
主页: http://www.gnu.org/software/libmicrohttpd/
Git: https://git.gnunet.org/libmicrohttpd.git
HTTP(S):https://ftp.gnu.org/gnu/libmicrohttpd/
FTP: ftp://ftp.gnu.org/gnu/libmicrohttpd/
编译与安装
libmicrohttpd 有提供编译好的二进制,可自行通过HTTP(S)或者FTP方式下载
官方编译版本使用的是 MT 选项,如果需要 MD,或者其他编译选项,可以下载源码,根据不同平台进行编译。
接口说明
Main API
//创建服务实例
MHD_start_daemon
//允许现有连接,不允许新的连接加入
MHD_quiesce_daemon
//结束服务实例
MHD_stop_daemon
//Add another client connection to the set of connections
MHD_add_connection
//获得当前进程select集合
MHD_get_fdset
//获得poll函数的超时时间
MHD_get_timeout
//启动对文件句柄的监督
MHD_run
//启动服务&select poll
MHD_run_from_select
Connection API
//获取连接的所有头信息
MHD_get_connection_values
//设置每个连接的头
MHD_set_connection_value
//获取错误的信息
MHD_set_panic_func
//处理转移字符
MHD_http_unescape
//查询一个连接中的头字段
MHD_lookup_connection_value
//把respons 放入返回队列
MHD_queue_response
//挂起连接
MHD_suspend_connection
//回复连接
MHD_resume_connection
Response API
//设置response参数
MHD_set_response_options
//通过回调创建Response
MHD_create_response_from_callback
//通过data指针创建返回结果
MHD_create_response_from_data
//通过buffer创建返回结果
MHD_create_response_from_buffer
//通过buffer和回调创建返回结果
MHD_create_response_from_buffer_with_free_callback
//通过文件描述符来创建返回结果
MHD_create_response_from_fd
//升级协议,容许我们自己多一些事情(如websocket)
MHD_upgrade_action
//创建升级协议时的返回内容
MHD_create_response_for_upgrade
//销毁response
MHD_destroy_response
//为response添加Header
MHD_add_response_header
//在response中添加页脚
MHD_add_response_footer
//在response中删除头
MHD_del_response_header
//获取
MHD_get_response_headers
PostProcessor functions
MHD_create_post_processor
MHD_post_process
MHD_destroy_post_processor
Digest Authentication functions
MHD_digest_auth_get_username
MHD_digest_auth_check
MHD_queue_auth_fail_response
MHD_basic_auth_get_username_password
generic query functions
MHD_get_connection_info
MHD_set_connection_option
MHD_get_daemon_info
MHD_get_version
MHD_is_feature_supported
示例
以下是GNU/Linux(包括在发行版中)系统下的一个简单示例:
#include <microhttpd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define PAGE "<html><head><title>libmicrohttpd demo</title>"\
"</head><body>libmicrohttpd demo</body></html>"
static int ahc_echo(void * cls,
struct MHD_Connection * connection,
const char * url,
const char * method,
const char * version,
const char * upload_data,
size_t * upload_data_size,
void ** ptr) {
static int dummy;
const char * page = cls;
struct MHD_Response * response;
int ret;
if (0 != strcmp(method, "GET"))
return MHD_NO; /* unexpected method */
if (&dummy != *ptr)
{
/* The first time only the headers are valid,
do not respond in the first round... */
*ptr = &dummy;
return MHD_YES;
}
if (0 != *upload_data_size)
return MHD_NO; /* upload data in a GET!? */
*ptr = NULL; /* clear context pointer */
response = MHD_create_response_from_buffer (strlen(page),
(void*) page,
MHD_RESPMEM_PERSISTENT);
ret = MHD_queue_response(connection,
MHD_HTTP_OK,
response);
MHD_destroy_response(response);
return ret;
}
int main(int argc,
char ** argv) {
struct MHD_Daemon * d;
if (argc != 2) {
printf("%s PORT\n", argv[0]);
return 1;
}
d = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION,
atoi(argv[1]),
NULL,
NULL,
&ahc_echo,
PAGE,
MHD_OPTION_END);
if (d == NULL)
return 1;
(void) getc (stdin);
MHD_stop_daemon(d);
return 0;
}
上面的例子使用最简单的线程模式MHD_USE_THREAD_PER_CONNECTION
。在这种模式下,MHD启动一个线程在端口上侦听新连接,然后生成一个新线程来处理每个连接。如果HTTP服务器几乎没有连接之间共享的任何状态(没有同步问题!),则此模式非常有用。并且该模式可能需要执行阻止操作(例如扩展的IO或代码运行)来处理单个连接。
可以看到MHD_start_daemon
函数启动服务,并且需要一个回调入参 ahc_echo
,注意此方法是非阻塞的,此示例使用getc
函数等待用户输入终止。
ahc_echo
回到函数在每次请求的时候都会进入。使用MHD_create_response_from_buffer
函数创建一个响应结构体,然后使用MHD_queue_response
函数把响应结构推入响应队列,libmicrohttpd 会自动执行响应。最后使用MHD_destroy_response
清理内存。
main函数
main函数非常简单,核心调用只有2个函数:MHD_start_daemon
,MHD_stop_daemon
,分别开始和停止http服务器。
int main()
{
const int port = 8888;
struct MHD_Daemon* daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, port , NULL, NULL, connectionHandler, NULL, MHD_OPTION_END);
if (daemon == NULL) {
std::cout << "cannot start server!\n";
return -1;
}
std::cin.get();
MHD_stop_daemon(daemon);
return 0;
}
MHD_start_daemon
函数包含非常多的参数,这也意味着它集成了很多的功能,这里只关注四个参数,其它都为NULL:
-
MHD_USE_INTERNAL_POLLING_THREAD
。这个参数与其他两个参数(MHD_USE_POLL_INTERNAL_THREAD、MHD_USE_EPOLL_INTERNAL_THREAD)
一起构成了microhttpd支持的三种模式:select、poll、epoll。用户必须选择其中之一。具体信息见源码。 -
port
。端口号。 -
connectHandler
。处理请求的函数。 -
MHD_OPTION_END
。由于MHD_start_daemon
最后一个参数是一个变参,因此MHD_OPTION_END
用来表示变参终止。
请求处理函数
所有的请求处理都发生在connectionHandler
中:
int connectionHandler( void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls)
{
const char* pageBuffer = "<html><body>Hello, I'm lgxZJ!</body></html>";
struct MHD_Response *response;
response = MHD_create_response_from_buffer(strlen(pageBuffer), (void*)pageBuffer,
MHD_RESPMEM_PERSISTENT);
if (MHD_add_response_header(response, "Content-Type", "text/html") == MHD_NO) {
std::cout << "MHD_add_response_header error\n";
return MHD_NO;
}
if (MHD_queue_response(connection, MHD_HTTP_OK, response) == MHD_NO) {
std::cout << "MHD_queue_response error\n";
return MHD_NO;
}
MHD_destroy_response(response);
return MHD_YES;
}
这个函数签名包含了所有用来处理请求的有用信息。microhttpd库提供了函数来方便我们响应请求,这里重点看创建响应。microhttpd库提供了两种方法来创建请求:从buffer创建、从文件创建。但是后者需要传入一个文件描述符,这在windows上不是很方便。
这里用缓冲创建。需要注意的是最后一个参数,这是一个MHD_ResponseMemoryMode
枚举值,表示使用的buffer内容是固定不变的。这种枚举类型还包含其他2种代表瞬时缓冲类型的值,分别表示缓冲区是在heap上的,和非heap(例如stack)上的。 用不同的缓冲区时要记得用不同的枚举值。 接下来设置MIME类型,把缓冲入队,并释放MHD_Response结构体。对于正确响应,返回MHD_YES
;不能响应的,返回MHD_NO
。
结果返回函数
创建返回的response,在抛给库后进行销毁。
static enum MHD_Result
send_page (struct MHD_Connection *connection, const char *page)
{
enum MHD_Result ret;
struct MHD_Response *response;
response = MHD_create_response_from_buffer (strlen (page), (void *) page,
MHD_RESPMEM_PERSISTENT);
if (! response)
return MHD_NO;
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
}
销毁函数
用户销毁开辟的相应内存以及相应资源。
static void
request_completed (void *cls, struct MHD_Connection *connection,
void **con_cls, enum MHD_RequestTerminationCode toe)
{
struct connection_info_struct *con_info = *con_cls;
if (NULL == con_info)
return;
if (con_info->connectiontype == POST)
{
MHD_destroy_post_processor (con_info->postprocessor);
if (con_info->answerstring)
free (con_info->answerstring);
}
free (con_info);
*con_cls = NULL;
}
创建服务daemon
通过相关接口创建服务实例
struct MHD_Daemon *
MHD_start_daemon (unsigned int flags,
uint16_t port,
MHD_AcceptPolicyCallback apc,
void *apc_cls,
MHD_AccessHandlerCallback dh,
void *dh_cls,
...)
{
struct MHD_Daemon *daemon;
va_list ap;
va_start (ap,
dh_cls);
daemon = MHD_start_daemon_va (flags,
port,
apc,
apc_cls,
dh,
dh_cls,
ap);
va_end (ap);
return daemon;
}