基于libevent的webserver的实现

主要实现了静态文件访问、记录访问日志、文件目录列表
编译脚本:

gcc -Wall fasthttpd.c -o fasthttpd -levent

重启脚本:
[cod="shell"]
#!/bin/sh
ps -ef | grep fasthttpd | grep -v grep | awk '{print $2}' | xargs -t -i kill -9 {} >/dev/null 2>&1
$(pwd)/fasthttpd
[/code]


#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <time.h>

// 引入 libevent 头文件
#include <event.h>
#include <evhttp.h>

#define HOST_IP "127.0.0.1"
#define HOST_PORT 8080
#define REQUEST_TIME_OUT 3

#define DOCUMENT_ROOT "www"
#define BUFF_MAX_LEN 20000
#define SERVER_NAME "fasthttpd"
#define ACCESS_LOG "logs/access.log"
#define DEBUG_LOG "logs/server.log" /* 服务器调试日志 */
#define ERROR_LOG "logs/error.log" /* 服务器错误日志 */

void parser(char *s,char res[][255]);
static char *strtoupper( char *s );
static long filesize(const char *filename);
//static int file_exists(const char *filename);
static void mime_content_type( const char *name, char *ret );
int WriteLog( const char *message, unsigned int message_type );
static int is_dir(const char *filename);

static unsigned short g_is_log = 1;
static int g_log_fd = 0;
static char dir_root[20000];

struct http_req
{
char method[20];
char request_uri[500];
char http_version[100];
char client_ip[20];
char request_time[2000];

} http_req_line;


// 请求处理模块
void http_handler(struct evhttp_request *req, void *arg)
{
char buff[20000];
char real_path[20000];
char tmp[2000];
char content_type[2000];
int fd;
unsigned int http_status_code;

time_t timep;
struct tm *m;
struct stat info;

DIR *dir;
struct dirent *ptr;

struct evbuffer *buf;
buf = evbuffer_new();

// 分析URL参数
char *decode_uri = strdup((char*) evhttp_request_uri(req));
//struct evkeyvalq http_query;
//evhttp_parse_query(decode_uri, &http_query);
//free(decode_uri);

sprintf(http_req_line.request_uri,"%s",decode_uri);

// 从http头中获取参数,如果是GET传输,这里就可以取得action的值
//const char *request_value = evhttp_find_header(&http_query, "data");

// 返回给浏览器的信息
evhttp_add_header(req->output_headers, "Server", "fasthttp");
//evhttp_add_header(req->output_headers, "Connection", "keep-alive");
evhttp_add_header(req->output_headers, "Connection", "close");

// 取得请求时间
memset(&buff,0,sizeof(buff));
time(&timep);
m = localtime(&timep);
sprintf(http_req_line.request_time,"%4d-%02d-%02d %02d:%02d:%02d",(1900+m->tm_year),(1+m->tm_mon),m->tm_mday,m->tm_hour,m->tm_min,m->tm_sec);

// 获取请求的服务器文件路径
memset(&real_path,0,sizeof(real_path));
sprintf(real_path,"%s/%s%s",dir_root,DOCUMENT_ROOT,http_req_line.request_uri);


if(stat(real_path,&info) == -1)
{
memset(&buff,0,sizeof(buff));

if (errno == ENOENT)
{
evhttp_send_error(req,404,"HTTP/1.1 404 Not Found");
sprintf(buff,"HTTP/1.1 404 Not Found\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
}
else if(access(real_path,R_OK) < 0)
{
evhttp_send_error(req,403,"HTTP/1.1 403 Forbidden");
sprintf(buff,"HTTP/1.1 403 Forbidden\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
}
else
{
evhttp_send_error(req,500,"HTTP/1.1 500 Server Error");
sprintf(buff,"HTTP/1.1 500 Server Error\t%s\t%s\n",http_req_line.request_uri,http_req_line.request_time);
}

evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=UTF-8");
WriteLog(buff,0);
}
else if(S_ISREG(info.st_mode))
{
http_status_code = 200;
mime_content_type(real_path,content_type);

memset(&tmp,0,sizeof(tmp));
fd = open(real_path,O_RDONLY);
read(fd,tmp,filesize(real_path));
close(fd);

sprintf(buff,"%s; charset=UTF-8",content_type);

// 记录访问日志
memset(&buff,0,sizeof(buff));
sprintf(buff,"HTTP/1.1 200 OK\t%s\t%ld\t%s\n",http_req_line.request_uri,filesize(real_path),http_req_line.request_time);
WriteLog(buff,0);

evhttp_add_header(req->output_headers, "Content-Type", buff);
evbuffer_add_printf(buf, "%s", tmp);
// 输出内容到浏览器
evhttp_send_reply(req, HTTP_OK, "OK", buf);
}
else if(S_ISDIR(info.st_mode))
{
http_status_code = 200;
memset(&tmp,0,sizeof(tmp));
memset(&buff,0,sizeof(buff));
sprintf(tmp,"<html><head><title>Index of %s</title></head><body><h1>Index of %s</h1><ul><li><a href=\"/\"> Parent Directory</a></li>",http_req_line.request_uri,http_req_line.request_uri);
strcat(buff,tmp);

if((dir = opendir(real_path)) != NULL)
{
while((ptr = readdir(dir)) != NULL)
{
if(strcmp(ptr->d_name,".") == 0 || strcmp(ptr->d_name,"..") == 0)
{
continue;
}

memset(&tmp,0,sizeof(tmp));
sprintf(tmp,"%s/%s",real_path,ptr->d_name);

if(is_dir(tmp))
{
memset(&tmp,0,sizeof(tmp));
sprintf(tmp,"<li><a href=\"%s/\"> %s/</a></li>",ptr->d_name,ptr->d_name);
}
else
{
memset(&tmp,0,sizeof(tmp));
sprintf(tmp,"<li><a href=\"%s\"> %s</a></li>",ptr->d_name,ptr->d_name);
}
strcat(buff,tmp);
}

closedir(dir);
}

memset(&tmp,0,sizeof(tmp));
sprintf(tmp,"%s","</ul>");
strcat(buff,tmp);

evhttp_add_header(req->output_headers, "Content-Type", "text/html; charset=UTF-8");
evbuffer_add_printf(buf, "%s", buff);
// 输出内容到浏览器
evhttp_send_reply(req, HTTP_OK, "OK", buf);
}

// 内存释放
//evhttp_clear_headers(&http_query);
evbuffer_free(buf);
}

int main(int argc, char **argv)
{
int timeout = 3;

getcwd(dir_root,sizeof(dir_root));

struct evhttp *httpd;
event_init();

// 绑定本机ip和端口,在访问时一定要把8080端口开放出来
httpd = evhttp_start(HOST_IP, HOST_PORT);

if (httpd == NULL)
{
fprintf(stderr, "Error: Unable to listen on %s:%d\n\n", HOST_IP, HOST_PORT);
exit(1);
}

// 设置请求超时时间
evhttp_set_timeout(httpd, timeout);

// 设置请求的处理函数
evhttp_set_gencb(httpd, http_handler, NULL);
event_dispatch();
evhttp_free(httpd);

return 0;
}

void parser(char *s,char res[][255])
{
int i,j = 0;
int n;
// char hosts[255];

for (i = 0;s[i] != '\r';i++) /* obtain the first line in http protocol head */
;
s[i] = '\0';
n=i++;

for (i = 0,j = 0;i < 3;i++,j++) /* divide the protocol head in blank */
{
strcpy(res[j],strsep(&s," "));
}

// for(i=n;s[i] != '\r';i++)
// {
// strcat(hosts,s[i]);
// }
//
// for (i = 0,j = 0;i < 3;i++,j++) /* divide the protocol head in blank */
// {
// strcpy(host[j],strsep(&hosts,":"));
// }

}

/**
* strtoupper - string to upper
*
*/
static char *strtoupper( char *s )
{
int i, len = sizeof(s);
for( i = 0; i < len; i++ )
{
s[i] = ( s[i] >= 'a' && s[i] <= 'z' ? s[i] + 'A' - 'a' : s[i] );
}

return(s);
}

/**
* filesize - get file size
*/
static long filesize(const char *filename)
{
struct stat buf;
if (!stat(filename, &buf))
{
return buf.st_size;
}
return 0;
}

/**
* file_exists - check file is exist
*/
//static int file_exists(const char *filename)
//{
// struct stat buf;
//
// if (stat(filename, &buf) < 0)
// {
// if (errno == ENOENT)
// {
// return 0;
// }
// }
// return 1;
//}

/**
* Get MIME type header
*
*/
static void mime_content_type( const char *name, char *ret ){
char *dot, *buf;

dot = strrchr(name, '.');

/* Text */
if ( strcmp(dot, ".txt") == 0 ){
buf = "text/plain";
} else if ( strcmp( dot, ".css" ) == 0 ){
buf = "text/css";
} else if ( strcmp( dot, ".js" ) == 0 ){
buf = "text/javascript";
} else if ( strcmp(dot, ".xml") == 0 || strcmp(dot, ".xsl") == 0 ){
buf = "text/xml";
} else if ( strcmp(dot, ".xhtm") == 0 || strcmp(dot, ".xhtml") == 0 || strcmp(dot, ".xht") == 0 ){
buf = "application/xhtml+xml";
} else if ( strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0 || strcmp(dot, ".shtml") == 0 || strcmp(dot, ".hts") == 0 ){
buf = "text/html";

/* Images */
} else if ( strcmp( dot, ".gif" ) == 0 ){
buf = "image/gif";
} else if ( strcmp( dot, ".png" ) == 0 ){
buf = "image/png";
} else if ( strcmp( dot, ".bmp" ) == 0 ){
buf = "application/x-MS-bmp";
} else if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 || strcmp( dot, ".jpe" ) == 0 || strcmp( dot, ".jpz" ) == 0 ){
buf = "image/jpeg";

/* Audio & Video */
} else if ( strcmp( dot, ".wav" ) == 0 ){
buf = "audio/wav";
} else if ( strcmp( dot, ".wma" ) == 0 ){
buf = "audio/x-ms-wma";
} else if ( strcmp( dot, ".wmv" ) == 0 ){
buf = "audio/x-ms-wmv";
} else if ( strcmp( dot, ".au" ) == 0 || strcmp( dot, ".snd" ) == 0 ){
buf = "audio/basic";
} else if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 ){
buf = "audio/midi";
} else if ( strcmp( dot, ".mp3" ) == 0 || strcmp( dot, ".mp2" ) == 0 ){
buf = "audio/x-mpeg";
} else if ( strcmp( dot, ".rm" ) == 0 || strcmp( dot, ".rmvb" ) == 0 || strcmp( dot, ".rmm" ) == 0 ){
buf = "audio/x-pn-realaudio";
} else if ( strcmp( dot, ".avi" ) == 0 ){
buf = "video/x-msvideo";
} else if ( strcmp( dot, ".3gp" ) == 0 ){
buf = "video/3gpp";
} else if ( strcmp( dot, ".mov" ) == 0 ){
buf = "video/quicktime";
} else if ( strcmp( dot, ".wmx" ) == 0 ){
buf = "video/x-ms-wmx";
} else if ( strcmp( dot, ".asf" ) == 0 || strcmp( dot, ".asx" ) == 0 ){
buf = "video/x-ms-asf";
} else if ( strcmp( dot, ".mp4" ) == 0 || strcmp( dot, ".mpg4" ) == 0 ){
buf = "video/mp4";
} else if ( strcmp( dot, ".mpe" ) == 0 || strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpg" ) == 0 || strcmp( dot, ".mpga" ) == 0 ){
buf = "video/mpeg";

/* Documents */
} else if ( strcmp( dot, ".pdf" ) == 0 ){
buf = "application/pdf";
} else if ( strcmp( dot, ".rtf" ) == 0 ){
buf = "application/rtf";
} else if ( strcmp( dot, ".doc" ) == 0 || strcmp( dot, ".dot" ) == 0 ){
buf = "application/msword";
} else if ( strcmp( dot, ".xls" ) == 0 || strcmp( dot, ".xla" ) == 0 ){
buf = "application/msexcel";
} else if ( strcmp( dot, ".hlp" ) == 0 || strcmp( dot, ".chm" ) == 0 ){
buf = "application/mshelp";
} else if ( strcmp( dot, ".swf" ) == 0 || strcmp( dot, ".swfl" ) == 0 || strcmp( dot, ".cab" ) == 0 ){
buf = "application/x-shockwave-flash";
} else if ( strcmp( dot, ".ppt" ) == 0 || strcmp( dot, ".ppz" ) == 0 || strcmp( dot, ".pps" ) == 0 || strcmp( dot, ".pot" ) == 0 ){
buf = "application/mspowerpoint";

/* Binary & Packages */
} else if ( strcmp( dot, ".zip" ) == 0 ){
buf = "application/zip";
} else if ( strcmp( dot, ".rar" ) == 0 ){
buf = "application/x-rar-compressed";
} else if ( strcmp( dot, ".gz" ) == 0 ){
buf = "application/x-gzip";
} else if ( strcmp( dot, ".jar" ) == 0 ){
buf = "application/java-archive";
} else if ( strcmp( dot, ".tgz" ) == 0 || strcmp( dot, ".tar" ) == 0 ){
buf = "application/x-tar";
} else {
buf = "application/octet-stream";
}
strcpy(ret, buf);
}

/**
* Log message
*
*/
int WriteLog( const char *message,unsigned int message_type )
{
if ( !g_is_log )
{
fprintf(stderr, "%s", message);
return 0;
}

char g_log_path[2000];

getcwd(g_log_path, sizeof(g_log_path));
strcat(g_log_path,"/");

if(message_type == 0)
{
strcat(g_log_path,ACCESS_LOG);
}
else if(message_type == 1)
{
strcat(g_log_path,DEBUG_LOG);
}
else if(message_type == 2)
{
strcat(g_log_path,ERROR_LOG);
}
else
{
perror("error message type");
return -1;
}

if ( (g_log_fd = open(g_log_path, O_RDWR|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1 )
{
perror("open log file error");
return -1;
}

if (write(g_log_fd, message, strlen(message)) == -1)
{
perror("write log error");
return -1;
}

return 0;
}

/**
* is_dir - check file is directory
*
*/
static int is_dir(const char *filename){
struct stat buf;
if ( stat(filename, &buf) < 0 ){
return -1;
}
if (S_ISDIR(buf.st_mode)){
return 1;
}
return 0;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值