//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:[email protected]
//@brief:客户端请求分析
#include "lcw_shttpd.h"
extern void Error_400(struct worker_ctl* wctl);
extern void Error_403(struct worker_ctl* wctl);
extern void Error_404(struct worker_ctl* wctl);
extern void Error_505(struct worker_ctl* wctl);
static struct http_header http_headers[] =
{
{16, HDR_INT, OFFSET(cl), "Content-Length: " },
{14, HDR_STRING, OFFSET(ct), "Content-Type: " },
{12, HDR_STRING, OFFSET(useragent), "User-Agent: " },
{19, HDR_DATE, OFFSET(ims), "If-Modified-Since: " },
{15, HDR_STRING, OFFSET(auth), "Authorization: " },
{9, HDR_STRING, OFFSET(referer), "Referer: " },
{8, HDR_STRING, OFFSET(cookie), "Cookie: " },
{10, HDR_STRING, OFFSET(location), "Location: " },
{8, HDR_INT, OFFSET(status), "Status: " },
{7, HDR_STRING, OFFSET(range), "Range: " },
{12, HDR_STRING, OFFSET(connection), "Connection: " },
{19, HDR_STRING, OFFSET(transenc), "Transfer-Encoding: " },
{0, HDR_INT, 0, NULL }
};
extern struct vec _shttpd_methods[];
struct conf_opts conf_para;
/******************************************************
函数名:montoi(char *s)
参数:
功能:将月份转化为数字
*******************************************************/
static int montoi(char *s)
{
DBGPRINT("LCW==>montoi\n");
int retval = -1;
static char *ar[] =
{ //月份数组
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
size_t i;
for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
{
if (!strcmp(s, ar[i]))
{//遍历查找
retval = i;
goto EXITmontoi;
}
}
DBGPRINT("LCW<==montoi\n");
EXITmontoi:
return retval;
}
/******************************************************
函数名:date_to_epoch(char *s)
参数:
功能:Parse date-time string, and return the corresponding time_t value
*******************************************************/
static time_t date_to_epoch(char *s)
{
DBGPRINT("LCW==>date_to_epoch\n");
struct tm tm;
char mon[32];
int sec, min, hour, mday, month, year;
(void) memset(&tm, 0, sizeof(tm));
sec = min = hour = mday = month = year = 0;
if (((sscanf(s, "%d/%3s/%d %d:%d:%d",&mday, mon, &year, &hour, &min, &sec) == 6) ||
(sscanf(s, "%d %3s %d %d:%d:%d",&mday, mon, &year, &hour, &min, &sec) == 6) ||
(sscanf(s, "%*3s, %d %3s %d %d:%d:%d", &mday, mon, &year, &hour, &min, &sec) == 6) ||
(sscanf(s, "%d-%3s-%d %d:%d:%d",&mday, mon, &year, &hour, &min, &sec) == 6)) && (month = montoi(mon)) != -1)
{
tm.tm_mday = mday;
tm.tm_mon = month;
tm.tm_year = year;
tm.tm_hour = hour;
tm.tm_min = min;
tm.tm_sec = sec;
}
if (tm.tm_year > 1900)
tm.tm_year -= 1900;
else if (tm.tm_year < 70)
tm.tm_year += 100;
DBGPRINT("LCW<==date_to_epoch\n");
return (mktime(&tm));
}
/******************************************************
函数名:Request_HeaderParse(char *s, int len, struct headers *parsed)
参数:
功能:
*******************************************************/
void Request_HeaderParse(char *s, int len, struct headers *parsed)
{
DBGPRINT("LCW==>Request_HeaderParse\n");
struct http_header *h;//结构struct http_header指针
union variant *v;//通用参数
char *p,*e = s + len;//p当前位置,e尾部
// 查找请求字符串中的头部关键字
while (s < e)
{
for (p = s; p < e && *p != '\n'; ) //查找一行末尾
{
p++;
}
for (h = http_headers; h->len != 0; h++)//已知方法
{ //最上面定义的那个结构体数组
if (e - s > h->len && !strncasecmp(s, h->name, h->len))//字符串匹配
{
break;
}
}
if (h->len != 0) //将此方法放入
{
s += h->len;//请求字符串中值的位置
v = (union variant *) ((char *) parsed + h->offset);//将值存放到参数parsed中
// 根据头部选项不同,计算不同的值*/
if (h->type == HDR_STRING) //字符串类型
{
v->v_vec.ptr = s;//字符串开始
v->v_vec.len = p - s;//字符串长度
if (p[-1] == '\r' && v->v_vec.len > 0)
{
v->v_vec.len--;
}
}
else if (h->type == HDR_INT) //INT类型
{
v->v_big_int = strtoul(s, NULL, 10);
}
else if (h->type == HDR_DATE) //时间格式
{
v->v_time = date_to_epoch(s);
}
}
s = p + 1;//转到下一个头部
}
DBGPRINT("LCW<==Request_HeaderParse\n");
}
#define JUMPOVER_CHAR(p,over) do{for(;*p== over;p++);}while(0);
#define JUMPTO_CHAR(p,to) do{for(;* to;p++);}while(0);
/******************************************************
函数名:Request_Parse(struct worker_ctl *wctl)
参数:
功能:分析获得的请求
*******************************************************/
int Request_Parse(struct worker_ctl *wctl)
{
DBGPRINT("LCW==>Request_Parse\n");
struct worker_conn *c = &wctl->conn;
struct conn_request *req = &c->con_req;
struct conn_response *res = &c->con_res;
int retval = 200;
char *p = req->req.ptr;
int len = req->req.len;
char *pos = NULL;
//处理第一行
//[GET /root/default.html HTTP/1.1\r\n]*/
//查找一行末尾
pos = memchr(p, '\n', len);
if(*(pos-1) == '\r')
{
*(pos-1) = '\0';
}
*pos = '\0';
pos = p;
//方法
int found = 0;
JUMPOVER_CHAR(pos,' ');//跳过空格
struct vec *m= NULL;
//查找比较方法字符串
for(m = &_shttpd_methods[0];m->ptr!=NULL;m++)
{
if(!strncmp(m->ptr, pos, m->len))//比较字符串
{
req->method = m->type;//更新头部方法
found = 1;
break;
}
}
if(!found)
{
retval = 400;
goto EXITRequest_Parse;
}
//URI分析*/
pos += m->len;//跳过方法
JUMPOVER_CHAR(pos,' ');//跳过空格
len -= pos -p;
p = pos;
JUMPTO_CHAR(pos, ' ');//跳到空格
*pos = '\0';
req->uri = (char*)p;
//文件
snprintf(req->rpath, URI_MAX, "%s/%s",conf_para.DocumentRoot, req->uri);
res->fd = open(req->rpath, O_RDONLY , 0644);
if(res->fd != -1)
{
fstat(res->fd, &res->fsate);
if(S_ISDIR(res->fsate.st_mode))
{
retval = 403;
goto EXITRequest_Parse;
}
}
else
{
retval = 404;
goto EXITRequest_Parse;
}
//HTTP版本:
// HTTP/[1|0].[1|0|9]
pos += 1;
JUMPOVER_CHAR(pos,' ');//跳过空格
len -= pos -p;
p = pos;
sscanf(p,"HTTP/%lu.%lu",&req->major, &req->minor);
if(!((req->major == 0 && req->minor == 9)||(req->major == 1 && req->minor == 0)||(req->major == 1 && req->minor == 1)))
{
retval = 505;
goto EXITRequest_Parse;
}
//其他头部信息
JUMPTO_CHAR(pos, '\0');
JUMPOVER_CHAR(pos,'\0');//跳过空字符
len -= pos - p;
p = pos;
Request_HeaderParse(p, len, & req->ch);
DBGPRINT("URI:'%s',patch:'%s'\n",req->uri,req->rpath);
EXITRequest_Parse:
DBGPRINT("LCW<==Request_Parse\n");
return retval;
}
/******************************************************
函数名:Request_Handle(struct worker_ctl* wctl)
参数:
功能:处理并响应客户端请求
*******************************************************/
int Request_Handle(struct worker_ctl* wctl)
{
DBGPRINT("LCW==>Request_Handle\n");
int err = wctl->conn.con_req.err;
int cs = wctl->conn.cs;
int cl = -1;
char *ptr = wctl->conn.con_res.res.ptr;
int len = -1;
int n = -1;
switch(err)
{
case 200:
Method_Do(wctl);
int fd = wctl->conn.con_res.fd;
cl = wctl->conn.con_res.cl;
len = strlen(wctl->conn.con_res.res.ptr);
n = write(cs, ptr, len);
printf("echo header:%s, write to client %d bytes, status:%d\n",ptr,n,wctl->conn.con_res.status);
if(fd != -1)
{
lseek(fd, 0, SEEK_SET);
len = sizeof(wctl->conn.dres);
printf("response len:%d, content length:%d\n",len,wctl->conn.con_res.cl);
for(n = 0; cl>0; cl -= n)
{
n = read(fd,ptr,len>cl?cl:sizeof(wctl->conn.dres));
printf("read %d bytes,",n);
if(n > 0)
{
n =write(cs, ptr, n);
printf("write %d bytes\n",n);
}
}
close(fd);
wctl->conn.con_res.fd = -1;
}
break;
default:
case 400:
Error_400(wctl);
cl = wctl->conn.con_res.cl;
len = strlen(wctl->conn.con_res.res.ptr);
n = write(cs, ptr, len);
break;
case 403:
Error_403(wctl);
cl = wctl->conn.con_res.cl;
len = strlen(wctl->conn.con_res.res.ptr);
n = write(cs, ptr, len);
break;
case 404:
Error_404(wctl);
cl = wctl->conn.con_res.cl;
len = strlen(wctl->conn.con_res.res.ptr);
n = write(cs, ptr, len);
break;
case 505:
Error_505(wctl);
cl = wctl->conn.con_res.cl;
len = strlen(wctl->conn.con_res.res.ptr);
n = write(cs, ptr, len);
break;
}
DBGPRINT("LCW<==Request_Handle\n");
return 0;
}