[原创]自己动手写 HTTP Server - heiyeluren的blog(黑夜路人的开源世界) - CSDNBlog
来源:heiyeluren的blog(黑夜路人的开源世界) - CSDNBlog
作者:heiyeluren
博客:http://blog.csdn.net/heiyeshuwu
代码:http://code.google.com/p/heiyeluren/downloads
时间:2008-06-22
【 原理 】 一般来说,HTTP Server 也是我们常说的Web服务器,大名鼎鼎的 Apache,还是微软的 IIS (Internet Information Server),开源领域的有 Lighttpd 和最近风头正劲的 Nginx 都是典型的Web服务器。最近想想能不能做一个Web服务器,就提供简单的功能,但是速度很快,能够作为一个专门处理 HTML/CSS/JS/Image 的Web服务器,那样能够让静态资源文件迅速的被访问到,如果有反向代理功能就更帅了,当然了,要是有Cache功能啥的,并且能够编写自定义插件(扩展)就很完美了。。。YY中。。。
基于这个思想,我就花十天时间使用标准C写了一个千行代码小型的HTTP Server,当然目前还不具有反向代理和扩展功能,只是能够简单的支持 HTML/CSS/JS/Image 静态资源文件的访问。HTTP Server 的名字叫做 tmhttpd - TieMa (Tiny&Mini) HTTP Server,小巧,代码少,执行速度快,目前具有的功能包括:
Support GET/HEAD method
The common MIME types.
Support Self custom default index page
Directory listings.
Support access log
Support Self custom port and max clients
Use fork mode accept new conntion
Support daemon type work
more ..
目前已经发布了 tmhttpd-1.0.0_alpha 版本,包括for Unix/Linux (在 Ubuntu/Fedoar/FreeBSD 下编译通过) 和 在cygwin环境下编译的 for Windows 版本,下面地址有下载:
Unix/Linux: http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha.tar.gz
Windows: http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha-win32.zip
下面大致来聊聊怎么写一个 HTTP Server,先看看一个HTTP请求的流程:
大致就是 客户端的浏览器(IE、Firefox、Opera、Lynx、Curl、Robot ...) 访问到Web服务器的端口(一般是80),然后端口接受到请求后开始解析请求,如果请求不正确或者非法则直接返回指定的错误代码(可以参考 RFC 1945),如果请求合法,那么就查找相应用户请求的文件,把文件读取后发送给客户端,关闭连接,请求结束。
【 实现 】
贴部分tmhttpd核心代码来描述这个过程:
view plaincopy to clipboardprint?
/**
* 初始化服务器端的Socket连接,等待连接,连接成功后fork新进程处理
*
*/
static void InitServerListen( unsigned int port, unsigned int max_client ){
int serversock, clientsock;
struct sockaddr_in server_addr, client_addr;
char currtime[32];
/* Create the TCP socket */
if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
die("Failed to create socket");
}
/* Construct the server sockaddr_in structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Clear struct */
server_addr.sin_family = AF_INET; /* Internet/IP */
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
server_addr.sin_port = htons(port); /* server port */
/* Bind the server socket */
if (bind(serversock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0){
die("Failed to bind the server socket");
}
/* Listen on the server socket */
if (listen(serversock, max_client) < 0){
die("Failed to listen on server socket");
}
/* Print listening message */
getdate(currtime);
fprintf(stdout, "[%s] Start server listening at port %d ...\n", currtime, port);
fprintf(stdout, "[%s] Waiting client connection ...\n", currtime);
/* Run until cancelled */
while (1){
unsigned int clientlen = sizeof(client_addr);
memset(currtime, 0, sizeof(currtime));
getdate(currtime);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){
die("Failed to accept client connection");
}
/* Use child process new connection */
if ( fork() == 0 ){
HandleClient(clientsock, client_addr);
} else {
wait(NULL);
}
/* Not use close socket connection */
close(clientsock);
}
}
/**
* 获取一个连接,读取连接客户端发送的请求数据,把请求数据叫给请求解析函数进行解析
*
*/
static void HandleClient( int client_sock, struct sockaddr_in client_addr ){
char buf[REQUEST_MAX_SIZE];
if ( read(client_sock, buf, REQUEST_MAX_SIZE) < 0){
SendError( client_sock, 500, "Internal Server Error", "", "Client request not success." );
die("read sock");
}
ParseRequest( client_sock, client_addr, buf );
close(client_sock);
exit(0);
}
/**
* 解析一个请求,解析出GET/HEAD方法,需要请求的文件,协议版本等,构造成结构体提交给处理函数
*
*/
static int ParseRequest( int client_sock, struct sockaddr_in client_addr, char *req ){
char **buf, **method_buf, **query_buf, currtime[32], cwd[1024], tmp_path[1536], pathinfo[512], path[256], file[256], log[1024];
int line_total, method_total, query_total, i;
struct st_request_info request_info;
/* Split client request */
getdate(currtime);
explode(req, '\n', &buf, &line_total);
/* Print log message */
memset(log, 0, sizeof(log));
sprintf(log, "[%s] %s %s\n", currtime, inet_ntoa(client_addr.sin_addr), buf[0]);
WriteLog(log);
/* Check request is empty */
if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0], "\r\n") == 0){
SendError( client_sock, 400, "Bad Request", "", "Can't parse request." );
}
/* Check method is implement */
explode(buf[0], ' ', &method_buf, &method_total);
if ( strcmp( strtolower(method_buf[0]), "get") != 0 && strcmp( strtolower(method_buf[0]), "head") != 0 ){
SendError( client_sock, 501, "Not Implemented", "", "That method is not implemented." );
}
explode(method_buf[1], '?', &query_buf, &query_total);
/* Make request data */
getcwd(cwd, sizeof(cwd));
strcpy(pathinfo, query_buf[0]);
substr(query_buf[0], 0, strrpos(pathinfo, '/')+1, path);
substr(query_buf[0], strrpos(pathinfo, '/')+1, 0, file);
/* Pad request struct */
memset(&request_info, 0, sizeof(request_info));
strcat(cwd, pathinfo);
request_info.method = method_buf[0];
request_info.pathinfo = pathinfo;
request_info.query = (query_total == 2 ? query_buf[1] : "");
request_info.protocal = strtolower(method_buf[2]);
request_info.path = path;
request_info.file = file;
request_info.physical_path = cwd;
/* Is a directory pad default index page */
memset(tmp_path, 0, sizeof(tmp_path));
strcpy(tmp_path, cwd);
if ( is_dir(tmp_path) ){
strcat(tmp_path, g_dir_index);
if ( file_exists(tmp_path) ){
request_info.physical_path = tmp_path;
}
}
/* Debug message */
if ( g_is_debug ){
fprintf(stderr, "[ Request ]\n");
for(i=0; i
fprintf(stderr, "%s\n", buf[i]);
}
}
/* Process client request */
ProcRequest( client_sock, client_addr, request_info );
return 0;
}
/**
* 处理函数按照解析出来的请求内容进行数据返回,返回文件/目录列表或者提示错误
*
*/
static int ProcRequest( int client_sock, struct sockaddr_in client_addr, struct st_request_info request_info ){
char buf[128];
/* File is exist or has access permission */
if ( !file_exists( request_info.physical_path ) ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s not found.", request_info.pathinfo);
SendError( client_sock, 404, "Not Found", "", buf);
}
if ( access(request_info.physical_path, R_OK) != 0 ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
/* Check target is regular file or directory */
if ( is_file(request_info.physical_path) == 1 ){
SendFile( client_sock, request_info.physical_path, request_info.pathinfo );
} else if ( is_dir(request_info.physical_path) == 1 ){
/* Is a directory choose browse dir list */
if ( g_is_browse ){
SendDirectory( client_sock, request_info.physical_path, request_info.pathinfo );
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
return 0;
}
/**
* 初始化服务器端的Socket连接,等待连接,连接成功后fork新进程处理
*
*/
static void InitServerListen( unsigned int port, unsigned int max_client ){
int serversock, clientsock;
struct sockaddr_in server_addr, client_addr;
char currtime[32];
/* Create the TCP socket */
if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
die("Failed to create socket");
}
/* Construct the server sockaddr_in structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Clear struct */
server_addr.sin_family = AF_INET; /* Internet/IP */
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
server_addr.sin_port = htons(port); /* server port */
/* Bind the server socket */
if (bind(serversock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0){
die("Failed to bind the server socket");
}
/* Listen on the server socket */
if (listen(serversock, max_client) < 0){
die("Failed to listen on server socket");
}
/* Print listening message */
getdate(currtime);
fprintf(stdout, "[%s] Start server listening at port %d ...\n", currtime, port);
fprintf(stdout, "[%s] Waiting client connection ...\n", currtime);
/* Run until cancelled */
while (1){
unsigned int clientlen = sizeof(client_addr);
memset(currtime, 0, sizeof(currtime));
getdate(currtime);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){
die("Failed to accept client connection");
}
/* Use child process new connection */
if ( fork() == 0 ){
HandleClient(clientsock, client_addr);
} else {
wait(NULL);
}
/* Not use close socket connection */
close(clientsock);
}
}
/**
* 获取一个连接,读取连接客户端发送的请求数据,把请求数据叫给请求解析函数进行解析
*
*/
static void HandleClient( int client_sock, struct sockaddr_in client_addr ){
char buf[REQUEST_MAX_SIZE];
if ( read(client_sock, buf, REQUEST_MAX_SIZE) < 0){
SendError( client_sock, 500, "Internal Server Error", "", "Client request not success." );
die("read sock");
}
ParseRequest( client_sock, client_addr, buf );
close(client_sock);
exit(0);
}
/**
* 解析一个请求,解析出GET/HEAD方法,需要请求的文件,协议版本等,构造成结构体提交给处理函数
*
*/
static int ParseRequest( int client_sock, struct sockaddr_in client_addr, char *req ){
char **buf, **method_buf, **query_buf, currtime[32], cwd[1024], tmp_path[1536], pathinfo[512], path[256], file[256], log[1024];
int line_total, method_total, query_total, i;
struct st_request_info request_info;
/* Split client request */
getdate(currtime);
explode(req, '\n', &buf, &line_total);
/* Print log message */
memset(log, 0, sizeof(log));
sprintf(log, "[%s] %s %s\n", currtime, inet_ntoa(client_addr.sin_addr), buf[0]);
WriteLog(log);
/* Check request is empty */
if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0], "\r\n") == 0){
SendError( client_sock, 400, "Bad Request", "", "Can't parse request." );
}
/* Check method is implement */
explode(buf[0], ' ', &method_buf, &method_total);
if ( strcmp( strtolower(method_buf[0]), "get") != 0 && strcmp( strtolower(method_buf[0]), "head") != 0 ){
SendError( client_sock, 501, "Not Implemented", "", "That method is not implemented." );
}
explode(method_buf[1], '?', &query_buf, &query_total);
/* Make request data */
getcwd(cwd, sizeof(cwd));
strcpy(pathinfo, query_buf[0]);
substr(query_buf[0], 0, strrpos(pathinfo, '/')+1, path);
substr(query_buf[0], strrpos(pathinfo, '/')+1, 0, file);
/* Pad request struct */
memset(&request_info, 0, sizeof(request_info));
strcat(cwd, pathinfo);
request_info.method = method_buf[0];
request_info.pathinfo = pathinfo;
request_info.query = (query_total == 2 ? query_buf[1] : "");
request_info.protocal = strtolower(method_buf[2]);
request_info.path = path;
request_info.file = file;
request_info.physical_path = cwd;
/* Is a directory pad default index page */
memset(tmp_path, 0, sizeof(tmp_path));
strcpy(tmp_path, cwd);
if ( is_dir(tmp_path) ){
strcat(tmp_path, g_dir_index);
if ( file_exists(tmp_path) ){
request_info.physical_path = tmp_path;
}
}
/* Debug message */
if ( g_is_debug ){
fprintf(stderr, "[ Request ]\n");
for(i=0; i
fprintf(stderr, "%s\n", buf[i]);
}
}
/* Process client request */
ProcRequest( client_sock, client_addr, request_info );
return 0;
}
/**
* 处理函数按照解析出来的请求内容进行数据返回,返回文件/目录列表或者提示错误
*
*/
static int ProcRequest( int client_sock, struct sockaddr_in client_addr, struct st_request_info request_info ){
char buf[128];
/* File is exist or has access permission */
if ( !file_exists( request_info.physical_path ) ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s not found.", request_info.pathinfo);
SendError( client_sock, 404, "Not Found", "", buf);
}
if ( access(request_info.physical_path, R_OK) != 0 ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
/* Check target is regular file or directory */
if ( is_file(request_info.physical_path) == 1 ){
SendFile( client_sock, request_info.physical_path, request_info.pathinfo );
} else if ( is_dir(request_info.physical_path) == 1 ){
/* Is a directory choose browse dir list */
if ( g_is_browse ){
SendDirectory( client_sock, request_info.physical_path, request_info.pathinfo );
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
return 0;
} 主要核心的函数就是这四个,如果要了解其他函数,包括字符串处理,HTTP头信息发送,错误发送,读取文件,遍历目录等等请下载源码回去研究。源码下载地址:http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha.tar.gz
【 结束 】
其实还有很多功能能够增加的,比如性能,采用 select 或者 epool 肯定要比单纯的fork性能更好,另外增加处理动态内容,比如CGI,能够接受POST请求,能够作为一个反向代理存在,能够具有缓存功能,能够缓存请求过的文件到内存中,能够具有插件扩展功能等等,都是需要进一步提升的。
参考文档:
RFC 1945: http://www.w3.org/Protocols/rfc1945/rfc1945
RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616.html
http://blog.csdn.net/heiyeshuwu/archive/2008/06/22/2576915.aspx
他们设置了哪些标签:
C heiyeshuwu Http HTTP_SERVER Server Web web服务器 WEB开发 with writing
谁收藏了这个网址:
时间:2008-6-23 10:31:47 | 相关网摘
时间:2008-6-23 10:59:39 | 相关网摘
时间:2008-6-23 12:30:46 | 相关网摘
使用标签:HTTP,时间:2008-6-23 12:45:32 | 相关网摘
一般来说,HTTP Server 也是我们常说的Web服务器,大名鼎鼎的 Apache,还是微软的 IIS (Internet Information Server),开源领域的有 Lighttpd 和最近风头正劲的 Nginx 都是典型的Web服务器。最近想想能不能做一个Web服务器,就提供简单的功能,但是速度很快,能够作为一个专门处理 HTML/CSS/JS/Image 的Web服务器,那样能够让静态资源文件迅速的被访问到,如果有反向代理功能就更帅了,当然了,要是有Cache功能啥的,并且能够编写自定义插件(扩展)就很完美了。。。YY中。。。
时间:2008-6-23 13:32:24 | 相关网摘
使用标签:Web服务器,时间:2008-6-23 13:48:17 | 相关网摘
【 原理 】 一般来说,HTTP Server 也是我们常说的Web服务器,大名鼎鼎的 Apache,还是微软的 IIS (Internet Information Server),开源领域的有 Lighttpd 和最近风头正劲的 Nginx 都是典型的Web服务器。最近想想能不能做一个Web服务器,就提供简单的功能,但是速度很快,能够作为一个专门处理 HTML/CSS/JS/Image 的Web服务器,那样能够让静态资源文件迅速的被访问到,如果有反向代理功能就更帅了,当然了,要是有Cache功能啥的,并且能够编写自定义插件(扩展)就很完美了。。。YY中。。。
使用标签:Web开发,时间:2008-6-23 14:18:59 | 相关网摘
来源:heiyeluren的blog(黑夜路人的开源世界) - CSDNBlog
作者:heiyeluren
博客:http://blog.csdn.net/heiyeshuwu
代码:http://code.google.com/p/heiyeluren/downloads
时间:2008-06-22
【 原理 】 一般来说,HTTP Server 也是我们常说的Web服务器,大名鼎鼎的 Apache,还是微软的 IIS (Internet Information Server),开源领域的有 Lighttpd 和最近风头正劲的 Nginx 都是典型的Web服务器。最近想想能不能做一个Web服务器,就提供简单的功能,但是速度很快,能够作为一个专门处理 HTML/CSS/JS/Image 的Web服务器,那样能够让静态资源文件迅速的被访问到,如果有反向代理功能就更帅了,当然了,要是有Cache功能啥的,并且能够编写自定义插件(扩展)就很完美了。。。YY中。。。
基于这个思想,我就花十天时间使用标准C写了一个千行代码小型的HTTP Server,当然目前还不具有反向代理和扩展功能,只是能够简单的支持 HTML/CSS/JS/Image 静态资源文件的访问。HTTP Server 的名字叫做 tmhttpd - TieMa (Tiny&Mini) HTTP Server,小巧,代码少,执行速度快,目前具有的功能包括:
Support GET/HEAD method
The common MIME types.
Support Self custom default index page
Directory listings.
Support access log
Support Self custom port and max clients
Use fork mode accept new conntion
Support daemon type work
more ..
目前已经发布了 tmhttpd-1.0.0_alpha 版本,包括for Unix/Linux (在 Ubuntu/Fedoar/FreeBSD 下编译通过) 和 在cygwin环境下编译的 for Windows 版本,下面地址有下载:
Unix/Linux: http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha.tar.gz
Windows: http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha-win32.zip
下面大致来聊聊怎么写一个 HTTP Server,先看看一个HTTP请求的流程:
大致就是 客户端的浏览器(IE、Firefox、Opera、Lynx、Curl、Robot ...) 访问到Web服务器的端口(一般是80),然后端口接受到请求后开始解析请求,如果请求不正确或者非法则直接返回指定的错误代码(可以参考 RFC 1945),如果请求合法,那么就查找相应用户请求的文件,把文件读取后发送给客户端,关闭连接,请求结束。
【 实现 】
贴部分tmhttpd核心代码来描述这个过程:
view plaincopy to clipboardprint?
/**
* 初始化服务器端的Socket连接,等待连接,连接成功后fork新进程处理
*
*/
static void InitServerListen( unsigned int port, unsigned int max_client ){
int serversock, clientsock;
struct sockaddr_in server_addr, client_addr;
char currtime[32];
/* Create the TCP socket */
if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
die("Failed to create socket");
}
/* Construct the server sockaddr_in structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Clear struct */
server_addr.sin_family = AF_INET; /* Internet/IP */
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
server_addr.sin_port = htons(port); /* server port */
/* Bind the server socket */
if (bind(serversock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0){
die("Failed to bind the server socket");
}
/* Listen on the server socket */
if (listen(serversock, max_client) < 0){
die("Failed to listen on server socket");
}
/* Print listening message */
getdate(currtime);
fprintf(stdout, "[%s] Start server listening at port %d ...\n", currtime, port);
fprintf(stdout, "[%s] Waiting client connection ...\n", currtime);
/* Run until cancelled */
while (1){
unsigned int clientlen = sizeof(client_addr);
memset(currtime, 0, sizeof(currtime));
getdate(currtime);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){
die("Failed to accept client connection");
}
/* Use child process new connection */
if ( fork() == 0 ){
HandleClient(clientsock, client_addr);
} else {
wait(NULL);
}
/* Not use close socket connection */
close(clientsock);
}
}
/**
* 获取一个连接,读取连接客户端发送的请求数据,把请求数据叫给请求解析函数进行解析
*
*/
static void HandleClient( int client_sock, struct sockaddr_in client_addr ){
char buf[REQUEST_MAX_SIZE];
if ( read(client_sock, buf, REQUEST_MAX_SIZE) < 0){
SendError( client_sock, 500, "Internal Server Error", "", "Client request not success." );
die("read sock");
}
ParseRequest( client_sock, client_addr, buf );
close(client_sock);
exit(0);
}
/**
* 解析一个请求,解析出GET/HEAD方法,需要请求的文件,协议版本等,构造成结构体提交给处理函数
*
*/
static int ParseRequest( int client_sock, struct sockaddr_in client_addr, char *req ){
char **buf, **method_buf, **query_buf, currtime[32], cwd[1024], tmp_path[1536], pathinfo[512], path[256], file[256], log[1024];
int line_total, method_total, query_total, i;
struct st_request_info request_info;
/* Split client request */
getdate(currtime);
explode(req, '\n', &buf, &line_total);
/* Print log message */
memset(log, 0, sizeof(log));
sprintf(log, "[%s] %s %s\n", currtime, inet_ntoa(client_addr.sin_addr), buf[0]);
WriteLog(log);
/* Check request is empty */
if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0], "\r\n") == 0){
SendError( client_sock, 400, "Bad Request", "", "Can't parse request." );
}
/* Check method is implement */
explode(buf[0], ' ', &method_buf, &method_total);
if ( strcmp( strtolower(method_buf[0]), "get") != 0 && strcmp( strtolower(method_buf[0]), "head") != 0 ){
SendError( client_sock, 501, "Not Implemented", "", "That method is not implemented." );
}
explode(method_buf[1], '?', &query_buf, &query_total);
/* Make request data */
getcwd(cwd, sizeof(cwd));
strcpy(pathinfo, query_buf[0]);
substr(query_buf[0], 0, strrpos(pathinfo, '/')+1, path);
substr(query_buf[0], strrpos(pathinfo, '/')+1, 0, file);
/* Pad request struct */
memset(&request_info, 0, sizeof(request_info));
strcat(cwd, pathinfo);
request_info.method = method_buf[0];
request_info.pathinfo = pathinfo;
request_info.query = (query_total == 2 ? query_buf[1] : "");
request_info.protocal = strtolower(method_buf[2]);
request_info.path = path;
request_info.file = file;
request_info.physical_path = cwd;
/* Is a directory pad default index page */
memset(tmp_path, 0, sizeof(tmp_path));
strcpy(tmp_path, cwd);
if ( is_dir(tmp_path) ){
strcat(tmp_path, g_dir_index);
if ( file_exists(tmp_path) ){
request_info.physical_path = tmp_path;
}
}
/* Debug message */
if ( g_is_debug ){
fprintf(stderr, "[ Request ]\n");
for(i=0; i
fprintf(stderr, "%s\n", buf[i]);
}
}
/* Process client request */
ProcRequest( client_sock, client_addr, request_info );
return 0;
}
/**
* 处理函数按照解析出来的请求内容进行数据返回,返回文件/目录列表或者提示错误
*
*/
static int ProcRequest( int client_sock, struct sockaddr_in client_addr, struct st_request_info request_info ){
char buf[128];
/* File is exist or has access permission */
if ( !file_exists( request_info.physical_path ) ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s not found.", request_info.pathinfo);
SendError( client_sock, 404, "Not Found", "", buf);
}
if ( access(request_info.physical_path, R_OK) != 0 ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
/* Check target is regular file or directory */
if ( is_file(request_info.physical_path) == 1 ){
SendFile( client_sock, request_info.physical_path, request_info.pathinfo );
} else if ( is_dir(request_info.physical_path) == 1 ){
/* Is a directory choose browse dir list */
if ( g_is_browse ){
SendDirectory( client_sock, request_info.physical_path, request_info.pathinfo );
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
return 0;
}
/**
* 初始化服务器端的Socket连接,等待连接,连接成功后fork新进程处理
*
*/
static void InitServerListen( unsigned int port, unsigned int max_client ){
int serversock, clientsock;
struct sockaddr_in server_addr, client_addr;
char currtime[32];
/* Create the TCP socket */
if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){
die("Failed to create socket");
}
/* Construct the server sockaddr_in structure */
memset(&server_addr, 0, sizeof(server_addr)); /* Clear struct */
server_addr.sin_family = AF_INET; /* Internet/IP */
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
server_addr.sin_port = htons(port); /* server port */
/* Bind the server socket */
if (bind(serversock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0){
die("Failed to bind the server socket");
}
/* Listen on the server socket */
if (listen(serversock, max_client) < 0){
die("Failed to listen on server socket");
}
/* Print listening message */
getdate(currtime);
fprintf(stdout, "[%s] Start server listening at port %d ...\n", currtime, port);
fprintf(stdout, "[%s] Waiting client connection ...\n", currtime);
/* Run until cancelled */
while (1){
unsigned int clientlen = sizeof(client_addr);
memset(currtime, 0, sizeof(currtime));
getdate(currtime);
/* Wait for client connection */
if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){
die("Failed to accept client connection");
}
/* Use child process new connection */
if ( fork() == 0 ){
HandleClient(clientsock, client_addr);
} else {
wait(NULL);
}
/* Not use close socket connection */
close(clientsock);
}
}
/**
* 获取一个连接,读取连接客户端发送的请求数据,把请求数据叫给请求解析函数进行解析
*
*/
static void HandleClient( int client_sock, struct sockaddr_in client_addr ){
char buf[REQUEST_MAX_SIZE];
if ( read(client_sock, buf, REQUEST_MAX_SIZE) < 0){
SendError( client_sock, 500, "Internal Server Error", "", "Client request not success." );
die("read sock");
}
ParseRequest( client_sock, client_addr, buf );
close(client_sock);
exit(0);
}
/**
* 解析一个请求,解析出GET/HEAD方法,需要请求的文件,协议版本等,构造成结构体提交给处理函数
*
*/
static int ParseRequest( int client_sock, struct sockaddr_in client_addr, char *req ){
char **buf, **method_buf, **query_buf, currtime[32], cwd[1024], tmp_path[1536], pathinfo[512], path[256], file[256], log[1024];
int line_total, method_total, query_total, i;
struct st_request_info request_info;
/* Split client request */
getdate(currtime);
explode(req, '\n', &buf, &line_total);
/* Print log message */
memset(log, 0, sizeof(log));
sprintf(log, "[%s] %s %s\n", currtime, inet_ntoa(client_addr.sin_addr), buf[0]);
WriteLog(log);
/* Check request is empty */
if (strcmp(buf[0], "\n") == 0 || strcmp(buf[0], "\r\n") == 0){
SendError( client_sock, 400, "Bad Request", "", "Can't parse request." );
}
/* Check method is implement */
explode(buf[0], ' ', &method_buf, &method_total);
if ( strcmp( strtolower(method_buf[0]), "get") != 0 && strcmp( strtolower(method_buf[0]), "head") != 0 ){
SendError( client_sock, 501, "Not Implemented", "", "That method is not implemented." );
}
explode(method_buf[1], '?', &query_buf, &query_total);
/* Make request data */
getcwd(cwd, sizeof(cwd));
strcpy(pathinfo, query_buf[0]);
substr(query_buf[0], 0, strrpos(pathinfo, '/')+1, path);
substr(query_buf[0], strrpos(pathinfo, '/')+1, 0, file);
/* Pad request struct */
memset(&request_info, 0, sizeof(request_info));
strcat(cwd, pathinfo);
request_info.method = method_buf[0];
request_info.pathinfo = pathinfo;
request_info.query = (query_total == 2 ? query_buf[1] : "");
request_info.protocal = strtolower(method_buf[2]);
request_info.path = path;
request_info.file = file;
request_info.physical_path = cwd;
/* Is a directory pad default index page */
memset(tmp_path, 0, sizeof(tmp_path));
strcpy(tmp_path, cwd);
if ( is_dir(tmp_path) ){
strcat(tmp_path, g_dir_index);
if ( file_exists(tmp_path) ){
request_info.physical_path = tmp_path;
}
}
/* Debug message */
if ( g_is_debug ){
fprintf(stderr, "[ Request ]\n");
for(i=0; i
fprintf(stderr, "%s\n", buf[i]);
}
}
/* Process client request */
ProcRequest( client_sock, client_addr, request_info );
return 0;
}
/**
* 处理函数按照解析出来的请求内容进行数据返回,返回文件/目录列表或者提示错误
*
*/
static int ProcRequest( int client_sock, struct sockaddr_in client_addr, struct st_request_info request_info ){
char buf[128];
/* File is exist or has access permission */
if ( !file_exists( request_info.physical_path ) ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s not found.", request_info.pathinfo);
SendError( client_sock, 404, "Not Found", "", buf);
}
if ( access(request_info.physical_path, R_OK) != 0 ){
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
/* Check target is regular file or directory */
if ( is_file(request_info.physical_path) == 1 ){
SendFile( client_sock, request_info.physical_path, request_info.pathinfo );
} else if ( is_dir(request_info.physical_path) == 1 ){
/* Is a directory choose browse dir list */
if ( g_is_browse ){
SendDirectory( client_sock, request_info.physical_path, request_info.pathinfo );
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
} else {
memset(buf, 0, sizeof(buf));
sprintf(buf, "File %s is protected.", request_info.pathinfo);
SendError( client_sock, 403, "Forbidden", "", buf);
}
return 0;
} 主要核心的函数就是这四个,如果要了解其他函数,包括字符串处理,HTTP头信息发送,错误发送,读取文件,遍历目录等等请下载源码回去研究。源码下载地址:http://heiyeluren.googlecode.com/files/tmhttpd-1.0.0_alpha.tar.gz
【 结束 】
其实还有很多功能能够增加的,比如性能,采用 select 或者 epool 肯定要比单纯的fork性能更好,另外增加处理动态内容,比如CGI,能够接受POST请求,能够作为一个反向代理存在,能够具有缓存功能,能够缓存请求过的文件到内存中,能够具有插件扩展功能等等,都是需要进一步提升的。
参考文档:
RFC 1945: http://www.w3.org/Protocols/rfc1945/rfc1945
RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616.html
时间:2008-6-23 15:00:26 | 相关网摘
时间:2008-6-23 15:47:24 | 相关网摘
时间:2008-6-23 16:40:07 | 相关网摘
时间:2008-6-23 17:32:35 | 相关网摘
时间:2008-6-23 20:11:35 | 相关网摘
时间:2008-6-24 9:31:09 | 相关网摘
时间:2008-6-24 12:43:38 | 相关网摘
时间:2008-6-24 15:38:47 | 相关网摘
httpserver
时间:2008-6-25 16:21:27 | 相关网摘
时间:2008-6-26 13:45:02 | 相关网摘
时间:2008-6-26 15:03:56 | 相关网摘
时间:2008-6-28 9:38:08 | 相关网摘
时间:2008-6-29 0:23:10 | 相关网摘
时间:2008-6-29 8:25:21 | 相关网摘
时间:2008-6-29 9:46:43 | 相关网摘
时间:2008-6-29 10:51:39 | 相关网摘
时间:2008-7-4 13:19:06 | 相关网摘
时间:2008-7-9 11:42:10 | 相关网摘