服务器SHTTPD可以识别的方法为GET、PUT、POST、DELETE和HEAD等,但仅实现了GET方法。在请求方法分析中已经可以获得客户端请求的方法,在响应中,只要匹配其方法就可以了。
(1)方法的总函数为Method_Do(),实现如下:
void Method_Do(struct worker_ctl *wctl)
{
DBGPRINT("==>Method_Do/n");
if(0)
Method_DoCGI(wctl);
switch(wctl->conn.con_req.method)
{
case METHOD_PUT:
Method_DoPut(wctl);
break;
case METHOD_DELETE:
Method_DoDelete(wctl);
break;
case METHOD_GET:
Method_DoGet(wctl);
break;
case METHOD_POST:
Method_DoPost(wctl);
break;
case METHOD_HEAD:
Method_DoHead(wctl);
break;
default:
Method_DoList(wctl);
}
DBGPRINT("<==Method_Do/n");
}
(2)请求结构的原型如下。主要用于解析客户端的请求,包括头部指针、uri指针、生成的真实地址、请求的类型、HTTP的主版本和副版本、头部结构等参数。代码如下:
struct conn_request{ /*请求结构*/
struct vec req; /*请求向量*/
char *head; /*请求头部'0'结尾*/
char *uri; /*请求URI,'0'结尾*/
char rpath[URI_MAX]; /*请求文件的真实地址'0'结尾*/
int method; /*请求类型*/
/*HTTP的版本信息*/
unsigned long major; /*主版本*/
unsigned long minor; /*副版本*/
struct headers ch; /*头部结构*/
struct worker_conn *conn; /*连接结构指针*/
int err; /*错误代码*/
};
(3)其中的头部结构表示不含第一行的其他客户端请求信息。包含常用的头部信息,例如内容长度、内容类型、连接状态、最后修改时间、用户名称、用户代理、参考、Cookie、位置、请求的内容范围、状态值、编码类型等,其中每个成员均为向量型变量,包含表达的字符串和长度。原型如下:
struct headers {
union variant cl; /*内容长度*/
union variant ct; /*内容类型*/
union variant connection; /*连接状态*/
union variant ims; /*最后修改时间*/
union variant user; /*用户名称*/
union variant auth; /*权限*/
union variant useragent; /*用户代理*/
union variant referer; /*参考*/
union variant cookie; /*Cookie*/
union variant location; /*位置*/
union variant range; /*范围*/
union variant status; /*状态值*/
union variant transenc; /*编码类型*/
};
(4)响应结构,主要包含由服务器发往客户端时用到的部分参数,例如建立时间、超时时间、响应的状态值、响应的内容长度、请求文件描述符、请求文件的状态等。原型 如下:
struct conn_response{ /*响应结构*/
struct vec res; /*响应向量*/
time_t birth_time; /*建立时间*/
time_t expire_time; /*超时时间*/
int status; /*响应状态值*/
int cl; /*响应内容长度*/
int fd; /*请求文件描述符*/
struct stat fsate; /*请求文件状态*/
struct worker_conn *conn; /*连接结构指针*/
};
其中Method_DoGet()函数整理得到响应的头部,并将客户端申请资源的内容长度放到成员变量cl中,便于之后的使用。Method_DoGet()函数的头部数据信息为如下形式的字 符串。
[HTTP/1.1 200 OK /*第一行*/
Date: Thu, 11 Dec 2008 11:25:33 GMT /*时间*/
Last-Modified: Wed, 12 Nov 2008 09:00:01 GMT /*修改时间*/
Etag: "491a2a91.2afe" /*Web资源标记号*/
Content-Type: text/plain /*文件类型*/
Content-Length: 11006 /*内容长度*/
Accept-Ranges: bytes] /*接收范围*/
(5)Method_DoGet()函数先初始化一些参数,例如状态值,状态信息等。
static int Method_DoGet(struct worker_ctl *wctl)
{
DBGPRINT("==>Method_DoGet/n");
struct conn_response *res = &wctl->conn.con_res;
struct conn_request *req = &wctl->conn.con_req;
char path[URI_MAX];
memset(path, 0, URI_MAX);
size_t n;
unsigned long r1, r2;
char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
/*需要确定的参数*/
size_t status = 200; /*状态值,已确定*/
char *msg = "OK"; /*状态信息,已确定*/
char date[64] = ""; /*时间*/
char lm[64] = ""; /*请求文件最后修改信息*/
char etag[64] = ""; /*etag信息*/
big_int_t cl; /*内容长度*/
char range[64] = ""; /*范围*/
struct mine_type *mine = NULL;
(6)之后获得构建头部需要的数据,例如当前时间、最后修改时间、ETAG、内容类型、内容长度及范围等数据。
/*当前时间*/
time_t t = time(NULL);
(void) strftime(date,
sizeof(date),
fmt,
localtime(&t));
/*最后修改时间*/
(void) strftime(lm,
sizeof(lm),
fmt,
localtime(&res->fsate.st_mtime));
/*ETAG*/
(void) snprintf(etag,
sizeof(etag),
"%lx.%lx",
(unsigned long) res->fsate.st_mtime,
(unsigned long) res->fsate.st_size);
/*发送的MIME类型*/
mine = Mine_Type(req->uri, strlen(req->uri), wctl);
/*内容长度*/
cl = (big_int_t) res->fsate.st_size;
/*范围range*/
memset(range, 0, sizeof(range));
n = -1;
if (req->ch.range.v_vec.len > 0 ) /*取出请求范围*/
{
printf("request range:%d/n",req->ch.range.v_vec.len);
n = sscanf(req->ch.range.v_vec.ptr,"bytes=%lu-%lu",&r1, &r2);
}
printf("n:%d/n",n);
if(n > 0)
{
status = 206;
(void) fseek(res->fd, r1, SEEK_SET);
cl = n == 2 ? r2 - r1 + 1: cl - r1;
(void) snprintf(range,
sizeof(range),
"Content-Range: bytes %lu-%lu/%lu/r/n",
r1,
r1 + cl - 1,
(unsigned long) res->fsate.st_size);
msg = "Partial Content";
}
(7)最后根据各种数据构建输出的头部数据。
/*构建输出的头部*/
memset(res->res.ptr, 0, sizeof(wctl->conn.dres));
snprintf(
res->res.ptr, /*缓冲区*/
sizeof(wctl->conn.dres), /*缓冲区长度*/
"HTTP/1.1 %d %s/r/n" /*状态和状态信息*/
"Date: %s/r/n" /*日期*/
"Last-Modified: %s/r/n" /*最后修改时间*/
"Etag: /"%s/"/r/n" /*Web资源标记号*/
"Content-Type: %.*s/r/n" /*内容类型*/
"Content-Length: %lu/r/n" /*内容长度*/
"Accept-Ranges: bytes/r/n" /*发送范围*/
"%s/r/n", /*范围起始*/
status, /*状态值*/
msg, /*状态信息*/
date, /*日期*/
lm, /*最后修改时间*/
etag, /*Web资源标记号*/
strlen(mine->mime_type), /*内容类型长度*/
mine->mime_type, /*内容类型*/
cl, /*内容长度*/
range); /*范围*/
res->cl = cl;
res->status = status;
DBGPRINT("<==Method_DoGet/n");
return 0;
}