一个简单的wed服务器SHTTPD(2)———— 客户端请求分析
//start from the very beginning,and to create greatness
//@author: Chuangwei Lin
//@E-mail:979951191@qq.com
//@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;
}
一个简单的wed服务器SHTTPD(2)———— 客户端请求分析的更多相关文章
- 一个简单的wed服务器SHTTPD(3)————SHTTPD多客户端支持的实现
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的wed服务器SHTTPD(9)————main函数文件,Makefile,头文件
主函数: #include "lcw_shttpd.h" //初始化时服务器的默认配置 extern struct conf_opts conf_para= { "/us ...
- 一个简单的wed服务器SHTTPD(5)————服务器SHTTPD请求方法解析
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析
开始学习<LInux网络编程>中的综合案例,虽然代码书上有,还是自己打一下加深理解和印象. 主要有两个函数,完成命令行的解析,另一个实现配置文件的解析,注释还是比较丰富的哦. //star ...
- 一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的wed服务器SHTTPD(6)———— SHTTPD错误处理的实现
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的wed服务器SHTTPD(8)———— URI分析
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的wed服务器SHTTPD(7)———— SHTTPD内容类型的实现
//start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...
- 一个简单的Web服务器-支持静态资源请求
目标 实现一个简单的Web服务器,能够根据HTTP请求的URL响应对应的静态资源,如果静态资源不存在则响应404. HttpServer 使用ServerSocket实现的一个服务器,request根 ...
随机推荐
- C语言 文件复制
有很多人会问,学会C语言能干啥?,就只能控制台敲个数学题,做个界面都没有的贪吃蛇么? 刚开始的我,也是这样想的,但慢慢深入C语言后,我才领略到C的强大,C的万能.小到游戏破解,加解密算法,大到设备驱动 ...
- 30.4 Map HashMap
本文将会讲解到: Map和Collection的对比 Map接口的使用,实现类HashMap的使用 /* * 需求:实现学号和姓名这样有对应关系的数据存储 * 为了体现这种有对应关系的数据,我们使用以 ...
- 28.3 api--date 日期 (日期获取、格式化)
/* * Date: 表示特定的瞬间,精确到毫秒,他可以通过方法来设定自己所表示的时间,可以表示任意的时间 * System.currentTimeMillis():返回的是当前系统时间,1970-1 ...
- [译]HAL-超文本应用语言
[译]HAL-超文本应用语言 精益超媒体类型 总结 HAL 是一种简单的格式,它提供了一种一致且简便的方法在 API 的资源之间进行超链接. 采用 HAL 将使您的 API 易于探索,并且其文档很容易 ...
- AJ学IOS 之微博项目实战(5)微博自定义搜索框searchBar
AJ分享,必须精品 一:效果 用UITextField简单定义一个搜索框 二:调用: 调用的代码,很简单,直接init就可以,以后加功能自己添加就行了. - (void)viewDidLoad { [ ...
- Supermarket POJ - 1456(贪心)
题目大意:n个物品,每个物品有一定的保质期d和一定的利润p,一天只能出售一个物品,问最大利润是多少? 题解:这是一个贪心的题目,有两种做法. 1 首先排序,从大到小排,然后每个物品,按保质期从后往前找 ...
- PHP函数:fopen
fopen() - 打开文件或者 URL. 注意:array_key_exists() 仅仅搜索第一维的键. 多维数组里嵌套的键不会被搜索到. 说明: fopen ( string $filenam ...
- 3. string
let str = "my string"; 1. str.startsWith('my'); //true2.str.endsWith('my'); //false3.str.i ...
- PHP代码审计理解(三)---EMLOG某插件文件写入
此漏洞存在于emlog下的某个插件---友言社会化评论1.3. 我们可以看到, uyan.php 文件在判断权限之前就可以接收uid参数.并且uid未被安全过滤即写入到了$uyan_code中. 我们 ...
- 开发一款图片压缩工具(二):使用 pngquant 实现图片压缩
上一篇我尝试使用了 pillow 库对 png 图片进行了压缩,效果不好.这次我换用 pngquant 来压缩.pngquant 是用于 PNG 图像有损压缩的命令行实用程序和库.压缩程序会显著减小文 ...