可编程http服务器

1 源码下载

http://pan.baidu.com/s/1hqvhxwK

2 特性

?  完全可定制的http服务器,可作为单独的Web服务器,可集成成到现有的工程中。

?  支持ROM页面,就是将网页文件转换成C代码,编译进可执行程序中,并且通过gzip压缩,这样大大降低服务器压力,适合嵌入式平台。

?  支持http基本认证和http摘要认证

?  可编程的CGI,比其他Web服务器的通过创建子进程的CGI效率更高,更易于与Web前端进行数据交互。

3 文件组织结构

libhttp-1.6

├── ev.c

├── ev.h

├── filelist.txt

├── functions.c

├── functions.h

├── libhttp.c

├── libhttp.h

├── list.h

├── main.c

├── Makefile

├── makeromfiles.c

├── md5.c

├── md5.h

└── web

     ├── css

     ├── images

     ├── index.html

     └── js 

4 directories, 14 files

 

 

ev.cev.h:来自于开源事件库libev源码,并进行了一定修改;

functions.cfunctions.h:一些功能辅助函数;

libhttp.clibhttp.h:处理http请求的相关代码;

list.h:从Linux内核拷贝而来,定义了一些链表和hash的相关操作;

md5.cmd5.hMD5加密相关的代码

web/:存储html网页文件;

web/css/:存储CSS网页文件

web/js:存储js网页文件

web/images:存储图片网页文件

filelist.txt:网页文件列表;

makeromfiles.c:用于将网页文件转换成C代码;

main.c:程序入口

Makefile:控制整个代码的编译过程

4 编译及运行

编译成X86版本,在源码目录下直接执行make

# make

 

编译嵌入式版本,比如arm版本

# make CROSS_COMPILE=arm-linux-

 

Makefile中将目标可执行程序命名为server,可自行修改。编译完成后,直接运行里面的server

# ./server

debug[http_server_start|1652]      libhttp 1.6 , authored by zjh

 

现在可以通过浏览器访问你的Web服务器了,在浏览器中输入你运行程序的设备的IP地址,然后回车

技术分享

技术分享

5 libev

libhttp使用了libev,因此首先需要熟悉libev的操作,有关libev的操作参考“libev手册.pdf”,下载地址:

http://pan.baidu.com/s/1hqvhxwK

6 添加网页文件

网页文件一律添加到源码下的web目录,一般将html文件放在web根目录,图片文件放在web/images目录,CSS文件放在web/css目录,JS文件放在web/js目录

 

例如添加图片test.jpg

libhttp-1.6/web/images/test.jpg

 

修改网页文件列表filelist.txt

web/index.html

 

web/images/test.jpg

 

 

然后直接make,并运行server,然后通过浏览器访问该图片,地址为:192.168.0.117/images/test.jpg,其中IP为你运行程序的设备的IP地址

技术分享

注意:如果仅仅修改了网页文件,需要执行make clean,然后重新执行make

7 CGI

下面是源码中默认的main.c

<span style="font-size:18px;">int main(int argc, char **argv)
{
	struct ev_loop *loop = EV_DEFAULT;
	http_server *srv = NULL;
	ev_signal sig_int, sig_pipe, sig_term, sig_quit;

	srv = http_server_new(80, NULL, loop);
	if (!srv)
		return -1;

	http_server_start(srv, NULL, 0, NULL);

	ev_signal_init(&sig_int, signal_cb, SIGINT);
	ev_signal_init(&sig_pipe, signal_cb, SIGPIPE);
	ev_signal_init(&sig_term, signal_cb, SIGTERM);
	ev_signal_init(&sig_quit, signal_cb, SIGQUIT);
	
	ev_signal_start(loop, &sig_int);
	ev_signal_start(loop, &sig_pipe);
	ev_signal_start(loop, &sig_term);
	ev_signal_start(loop, &sig_quit);

	// TODO:在此处添加自己的代码
	
	ev_run(loop, 0);

	http_server_free(srv);

	debug("server quit");

	return 0;
}</span>

 

添加CGI使用函数http_add_cgi(http_server * srv,char * name,void(* cb)(http_conn *conn))

 

下面是一个test例子

<span style="font-size:18px;">#include "libhttp.h"

void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
	if ((w->signum == SIGINT) || (w->signum == SIGTERM) || (w->signum == SIGQUIT))
	{
		ev_break(loop, EVBREAK_ALL);
	}
	else if (w->signum == SIGPIPE)
	{
		debug("Got SIGPIPE, Ignored");
	}
}

void cgi_test(http_conn *conn)
{
	int len;
	char buf[] = "<h1>libhttp CGI Test</h1>";

	http_set_status(conn, HTTP_STATUS_CODE_OK);
	http_add_header(conn, "Content-Type", "text/html");

	len = strlen(buf);
	http_add_header(conn, "Content-Length", (void *)len);
	http_send_header(conn);
	http_send_body(conn, buf, len, HTTP_SEND_BUFFER);
}

int main(int argc, char **argv)
{
	struct ev_loop *loop = EV_DEFAULT;
	http_server *srv = NULL;
	ev_signal sig_int, sig_pipe, sig_term, sig_quit;

	srv = http_server_new(80, NULL, loop);
	if (!srv)
		return -1;

	http_server_start(srv, NULL, 0, NULL);

	ev_signal_init(&sig_int, signal_cb, SIGINT);
	ev_signal_init(&sig_pipe, signal_cb, SIGPIPE);
	ev_signal_init(&sig_term, signal_cb, SIGTERM);
	ev_signal_init(&sig_quit, signal_cb, SIGQUIT);
	
	ev_signal_start(loop, &sig_int);
	ev_signal_start(loop, &sig_pipe);
	ev_signal_start(loop, &sig_term);
	ev_signal_start(loop, &sig_quit);

	// TODO:在此处添加自己的代码
	http_add_cgi(srv, "/test", cgi_test);
	
	ev_run(loop, 0);

	http_server_free(srv);

	debug("server quit");

	return 0;
}</span>


 

函数说明参考后面的API参考手册。

 

使用浏览器访问该cgi

技术分享

 

8 上传文件

首先添加上传文件的html文件libhttp-1.6/web/upload.html并修改网页文件列表filelist.txtupload.html内容如下:

<html>
	<head>
		<title>上传文件</title>
	</head>
	<body>
		<form action="/upload" method="POST" enctype ="multipart/form-data"> 
			<input name="file" type="file" /> 
			<input type="submit" value="上传"/>
		</form>
	</body>
</html>

然后修改main.c,添加上传文件的CGI,代码如下:

#include "libhttp.h"

void signal_cb(struct ev_loop *loop, ev_signal *w, int revents)
{
	if ((w->signum == SIGINT) || (w->signum == SIGTERM) || (w->signum == SIGQUIT))
	{
		ev_break(loop, EVBREAK_ALL);
	}
	else if (w->signum == SIGPIPE)
	{
		debug("Got SIGPIPE, Ignored");
	}
}

void cgi_upload(http_conn *conn)
{
	int len;
	char buf[1024] = "";
	char cwd[128], filename[128];
	int fd;

	getcwd(cwd, sizeof(cwd));
	snprintf(filename, sizeof(filename), "%s/%s", cwd, http_get_upload_filename(conn));

	fd = open(filename, O_CREAT | O_RDWR);
	if (fd < 0)
	{
		error_s("open");
		http_send_error(conn, HTTP_STATUS_CODE_INTERNAL_SERVER_ERROR);
		return;
	}

	while ((len = http_read_upload_file(conn, buf, sizeof(buf))) > 0)
	{
		write(fd, buf, len);
	}
	close(fd);

	snprintf(buf, sizeof(buf), 
		"<html>"
		"<body>"
		"<h1>文件名:%s</h1>"
		"<h1>文件大小:%.3f KB</h1>"
		"<h1>保存到:%s/%s</h1>"
		"</body>"
		"</html>"
		,http_get_upload_filename(conn)
		,http_get_upload_filesize(conn) / 1000.0
		,cwd, http_get_upload_filename(conn));
	

	http_set_status(conn, HTTP_STATUS_CODE_OK);
	http_add_header(conn, "Content-Type", "text/html");

	len = strlen(buf);
	http_add_header(conn, "Content-Length", (void *)len);
	http_send_header(conn);
	http_send_body(conn, buf, len, HTTP_SEND_BUFFER);
}

int main(int argc, char **argv)
{
	struct ev_loop *loop = EV_DEFAULT;
	http_server *srv = NULL;
	ev_signal sig_int, sig_pipe, sig_term, sig_quit;

	srv = http_server_new(80, NULL, loop);
	if (!srv)
		return -1;

	http_server_start(srv, NULL, 0, NULL);

	ev_signal_init(&sig_int, signal_cb, SIGINT);
	ev_signal_init(&sig_pipe, signal_cb, SIGPIPE);
	ev_signal_init(&sig_term, signal_cb, SIGTERM);
	ev_signal_init(&sig_quit, signal_cb, SIGQUIT);
	
	ev_signal_start(loop, &sig_int);
	ev_signal_start(loop, &sig_pipe);
	ev_signal_start(loop, &sig_term);
	ev_signal_start(loop, &sig_quit);

	// TODO:在此处添加自己的代码
	http_add_cgi(srv, "/upload", cgi_upload);
	
	ev_run(loop, 0);

	http_server_free(srv);

	debug("server quit");

	return 0;
}

使用浏览器访问upload.html

技术分享

点击浏览选择上传的文件

技术分享

点击上传

技术分享

9 http认证

9.1 基本认证

基本认证使用base64将用户名和密码进行编码发送,而base64编码很容易破解,所以基本认证可以说等同于明文传输用户名和密码。

 

修改main.c中的    http_server_start的调用参数,修改后如下

http_server_start(srv, NULL,HTTP_AUTH_BASIC, NULL);

 

然后添加要认证的网页,可以是单个网页、当个cgi,某个目录,或者某个路径前缀。例如要对index.html进行认证,代码如下:

http_add_auth_tag(srv,"/index.html");

 

如果要对所有分文件进行认证,代码如下:

http_add_auth_tag(srv, "/");

 

下面使用浏览器访问

技术分享

提示输入用户名和密码,并且是没有安全连接的基本认证,使用基本认证时,用户名和密码为你的系统用户名和密码。

9.2 摘要认证

摘要认证首先需要编码用户名和密码,格式为:MD5(username:realm:passwd),这里的usernamepasswd为你自己决定,realm为浏览器的某个标识,在libhttp.h中默认为"LIBHTTP-BY-ZJH",假设用户名为:zjh,密码为:123456,下面使用在线MD5编码,打开百度,搜索“MD5”

技术分享

选择红色方框里面的MD5加密

技术分享

得到32位密文,将它们全部转换成小写。然后修改修改main.c中的  http_server_start的调用参数,修改后如下

http_server_start(srv, NULL,HTTP_AUTH_DIGEST, "1d8ed8656030937244b3ad52d237fbdc");

 

测试如下:

技术分享

10 API参考手册

10.1 functions

参考源码中的functions.h

10.2 http

参考libhttp.h

11 BUG反馈

QQ809205580

Email[email protected]/[email protected]

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。