可编程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.c、ev.h:来自于开源事件库libev源码,并进行了一定修改;
functions.c、functions.h:一些功能辅助函数;
libhttp.c、libhttp.h:处理http请求的相关代码;
list.h:从Linux内核拷贝而来,定义了一些链表和hash的相关操作;
md5.c、md5.h:MD5加密相关的代码
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.txt,upload.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),这里的username和passwd为你自己决定,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反馈
QQ:809205580
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。