前面已经对请求行和请求头进行了处理,还有对URI也进行了处理,也处理了资源找不到和资源不允许访问的情况。下面讲一下,对于请求头几种头部字段的处理,头部字段名有这几个类型:Host,Connection,If-Modified-Since和什么都没有;它们都有各自的处理函数。
Host:初始URL中的主机和端口。
Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。
If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
举例看下一个http请求行和请求头,如下:
GET /home.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/testpage.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT
If-None-Match: "c561c68d0ba92bbeb8b0fff2a9199f722e3a621a"
Cache-Control: max-age=0
tk_http_handle_header(request, out);
void tk_http_handle_header(tk_http_request_t* request, tk_http_out_t* out){
//将处理结果存储在out当中
list_head* pos;//链表,存储一个请求的请求头中的头部字段名和值
tk_http_header_t* hd;//是链表里面的节点,结点存储关键字起始位置和值起始位置,还有指向链表的指针
tk_http_header_handle_t* header_in;//
int len;
list_for_each(pos, &(request->list)){
hd = list_entry(pos, tk_http_header_t, list);//获取链表入口位置
for(header_in = tk_http_headers_in; strlen(header_in->name) > 0; header_in++){
//将关键字依次与各种情况做比较,确定是哪个头部字段名,然后调用对应的处理函数。
if(strncmp(hd->key_start, header_in->name, hd->key_end - hd->key_start) == 0){
len = hd->value_end - hd->value_start;
(*(header_in->handler))(request, out, hd->value_start, len);
break;
}
}
list_del(pos);
free(hd);
}
}
上面用到了一个结构体,定义如下:
typedef struct tk_http_header_handle{
char* name;
tk_http_header_handler_pt handler; // 函数指针
}tk_http_header_handle_t;
也用到了逐个访问链表节点的函数。
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
也用到了
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) ); \
})
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
还用到了一个其他文件里的数组,通过extern加载。
typedef int (*tk_http_header_handler_pt)(tk_http_request_t* request, tk_http_out_t* out, char* data, int len);
//定义了一个函数指针,指向的函数,返回值是int,输入参数:tk_http_request_t指针,tk_http_out_t指针,char指针,整型变量
typedef struct tk_http_header_handle{
char* name;
tk_http_header_handler_pt handler; // 函数指针
}tk_http_header_handle_t;
tk_http_header_handle_t tk_http_headers_in[] = {
{"Host", tk_http_process_ignore},
{"Connection", tk_http_process_connection},
{"If-Modified-Since", tk_http_process_if_modified_since},//判断文件是否修改过
{"", tk_http_process_ignore}//什么都不做,返回0
};
typedef struct tk_http_out{
int fd;
int keep_alive;
time_t mtime;
int modified;
int status;
}tk_http_out_t;
对应的几种处理函数。
static int tk_http_process_ignore(tk_http_request_t* request, tk_http_out_t* out, char* data, int len){
(void) request;
(void) out;
(void) data;
(void) len;
return 0;
}
// 处理连接方式
static int tk_http_process_connection(tk_http_request_t* request, tk_http_out_t* out, char* data, int len){
(void) request;
// 记录请求是否为keep-alive
if (strncasecmp("keep-alive", data, len) == 0) {
out->keep_alive = 1;
}
return 0;
}
// 判断文件是否修改
static int tk_http_process_if_modified_since(tk_http_request_t* request, tk_http_out_t* out, char *data, int len){
(void) request;
(void) len;
struct tm tm;
// 转换data格式为GMT格式
if(strptime(data, "%a, %d %b %Y %H:%M:%S GMT", &tm) == (char*)NULL){
return 0;
}
// 将时间转换为自1970年1月1日以来持续时间的秒数
time_t client_time = mktime(&tm);
// 计算两个时刻之间的时间差
double time_diff = difftime(out->mtime, client_time);
// 微秒时间内未修改status显示未修改,modify字段置为1
if(fabs(time_diff) < 1e-6){
out->modified = 0;
out->status = TK_HTTP_NOT_MODIFIED;
}
return 0;
}
根据时间判断文件是否修改,使用了strptime函数,它是linux系统函数,将一个字符串表示的时间,转换成time时间。
#include <time.h>
char *strptime(const char *s, const char *format, struct tm *tm);
其中tm结构体如下。
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
然后用到了
double difftime(time_t time1, time_t time2)
返回 time1 和 time2 之间相差的秒数 (time1 - time2)。这两个时间是在日历时间中指定的,表示了自纪元 Epoch(协调世界时 UTC:1970-01-01 00:00:00)起经过的时间。