//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)———— 客户端请求分析的更多相关文章

  1. 一个简单的wed服务器SHTTPD(3)————SHTTPD多客户端支持的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  2. 一个简单的wed服务器SHTTPD(9)————main函数文件,Makefile,头文件

    主函数: #include "lcw_shttpd.h" //初始化时服务器的默认配置 extern struct conf_opts conf_para= { "/us ...

  3. 一个简单的wed服务器SHTTPD(5)————服务器SHTTPD请求方法解析

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  4. 一个简单的wed服务器SHTTPD(1)————命令行和文件配置解析

    开始学习<LInux网络编程>中的综合案例,虽然代码书上有,还是自己打一下加深理解和印象. 主要有两个函数,完成命令行的解析,另一个实现配置文件的解析,注释还是比较丰富的哦. //star ...

  5. 一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  6. 一个简单的wed服务器SHTTPD(6)———— SHTTPD错误处理的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  7. 一个简单的wed服务器SHTTPD(8)———— URI分析

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  8. 一个简单的wed服务器SHTTPD(7)———— SHTTPD内容类型的实现

    //start from the very beginning,and to create greatness //@author: Chuangwei Lin //@E-mail:979951191 ...

  9. 一个简单的Web服务器-支持静态资源请求

    目标 实现一个简单的Web服务器,能够根据HTTP请求的URL响应对应的静态资源,如果静态资源不存在则响应404. HttpServer 使用ServerSocket实现的一个服务器,request根 ...

随机推荐

  1. python 判断一个字符串是否是小数

    """练习判断一个小数1.判断是否合法2.合法需要有一个小数点3.小数点左边必须是个整数,右边必须是个正整数 """ def xiaoshu ...

  2. 21 static 静态的使用及特点

    /* * static:他是一个关键字,用来修饰成员变量和成员方法 * static特点: * 被所有的对象所共享 * 可以直接使用类名来调用 * 静态所修饰的成员加载优先于对象,随着类的加载而加载 ...

  3. python3(十二)functional

    # 函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数! # 变量可以指向函数 abs(-10) abs # <built-in function abs> ...

  4. 解决idea导入maven项目缺少jar包的问题

    之前一直用的elipse,现在用idea不熟悉,这里记录一下.这里以idea2017为例. 导入elipse的maven项目,提示缺少jar包,肯定是idea没有给你导包. 第一步,首先确认自己的id ...

  5. redis 正确实现分布式锁的正确方式

    前言 最近在自己所管理的项目中,发现redis加锁的方式不对,在高并发的情况有问题.故在网上找搜索了一把相关资料.发现好多都是互相抄袭的,很多都是有缺陷的.好多还在用redis 的 setnx命令来实 ...

  6. 墨者学院靶场:uWSGI(CVE-2018-7490)路径遍历漏洞复现

    0x01漏洞简介 uWSGI是一款Web应用程序服务器,它实现了WSGI.uwsgi和http等协议.uWSGI 2.0.17之前版本中存在路径遍历漏洞,该漏洞源于程序没有正确的处理DOCUMENT_ ...

  7. 深入理解new运算符

    在 JavaScript 中,new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例.创建一个对象很简单,为什么我们还要多此一举使用 new 运算符呢?它到底有什么样的魔力? 认 ...

  8. GeoGebra函数使用

    分段函数使用 输入指令: If(x < -2, x, -2 < x < 2, x², x > 2, x)

  9. K - Downgrade Gym - 101775K

    题目大意:一天不玩相当于A-B中将A转换为经验值,B舍弃掉,然后A=1,在通过升级所需要的经验值来判断可以升几级 题目连接:https://codeforces.com/gym/101775/prob ...

  10. .NET中 kafka消息队列、环境搭建与使用

    前面几篇文章中讲了一些关于消息队列的知识,就每中消息队列中间件,我们并没有做详细的讲解,那么,今天我们就来详细的讲解一下消息队列之一kafka的一些基本的使用与操作. 一.kafka介绍 kafka: ...