新增cache-pending接口函数tfe_get_cache_pending和tfe_put_cache_pending函数,用于

根据请求域和响应域判断是否应该缓存该HTTP会话
This commit is contained in:
lijie
2018-09-22 23:06:38 +08:00
committed by zhengchao
parent de94bc645c
commit 4ae103f755
3 changed files with 660 additions and 0 deletions

97
cache/include/tango_cache_pending.h vendored Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#include<time.h>
enum tfe_http_std_field
{
TFE_HTTP_UNKNOWN_FIELD = 0,
TFE_HTTP_HOST,
TFE_HTTP_REFERER,
TFE_HTTP_USER_AGENT,
TFE_HTTP_COOKIE,
TFE_HTTP_PROXY_AUTHORIZATION,
TFE_HTTP_AUTHORIZATION,
TFE_HTTP_LOCATION,
TFE_HTTP_SERVER,
TFE_HTTP_ETAG,
TFE_HTTP_DATE,
TFE_HTTP_TRAILER,
TFE_HTTP_TRANSFER_ENCODING,
TFE_HTTP_VIA,
TFE_HTTP_PRAGMA,
TFE_HTTP_CONNECTION,
TFE_HTTP_CONT_ENCODING,
TFE_HTTP_CONT_LANGUAGE,
TFE_HTTP_CONT_LOCATION,
TFE_HTTP_CONT_RANGE,
TFE_HTTP_CONT_LENGTH,
TFE_HTTP_CONT_TYPE,
TFE_HTTP_CONT_DISPOSITION,
TFE_HTTP_EXPIRES,
TFE_HTTP_ACCEPT_ENCODING,
TFE_HTTP_CACHE_CONTROL,
TLF_HTTP_IF_MATCH,
TLF_HTTP_IF_NONE_MATCH,
TLF_HTTP_IF_MODIFIED_SINCE,
TLF_HTTP_IF_UNMODIFIED_SINCE,
TLF_HTTP_LAST_MODIFIED
};
enum cache_pending_action {
UNDEFINED = 0,
ALLOWED,
FORBIDDEN,
VERIFY
};
struct tfe_http_field {
enum tfe_http_std_field http_field;
const char* value;
};
struct request_freshness {
time_t min_fresh;
time_t max_age;
};
struct response_freshness{
time_t date;
time_t last_modified;
time_t timeout;
};
/*
函数功能:
根据请求头字段判断是否允许将缓存作为该请求的响应,并且将请求字段对缓存新鲜度的约束范围作为传出参数返回给调用者
参数:
request:HTTP请求字段信息包括Pragma,Cache-Control,If-Match,If-None-Match,If-Modified-Since,If-Unmodified-Since字段
n_fields:request包含的HTTP请求字段数目
restrict:如果该函数返回值为ALLOWED则返回请求Cache-Control字段包括的min-fresh或者max-age值否则返回值为0
返回值:
UNDEFINED = 0,//请求字段中未定义缓存的行为
ALLOWED ,//允许使用缓存作为该请求的响应
FORBIDDEN,//禁止使用缓存作为该请求的响应,需要向源服务器请求
VERIFY,//禁止使用未验证有效性的缓存作为该请求的响应
*/
enum cache_pending_action tfe_cache_get_pending(const struct tfe_http_field *request, size_t n_fields,struct request_freshness* restrict);
/*
函数功能:
根据响应字段判断该响应是否允许被缓存并且将响应的新鲜度、date或者last-modified值作为传出参数返回给调用者
参数:
response:HTTP响应字段信息包括Pragma,Cache-Control,Expires、Date、Last-Modified字段
n_fields:response包含的HTTP响应字段数目
freshness:如果该函数返回值为ALLOWED则返回响应Cache-Control字段的s-maxage/max-age值以及Date字段、Last-Modified字段相对时间
返回值:
UNDEFINED = 0,//响应字段中未定义缓存的行为
ALLOWED ,//允许缓存该响应
FORBIDDEN,//禁止缓存该响应
*/
enum cache_pending_action tfe_cache_put_pending(const struct tfe_http_field *response, size_t n_fields, struct response_freshness* freshness);

352
cache/src/tango_cache_pending.cpp vendored Normal file
View File

@@ -0,0 +1,352 @@
#include"proxy_cache.h"
#include<assert.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>
time_t get_time_value(const char* field_value, const char* field_type)
{
time_t time;
char* time_value = NULL;
field_value += strlen(field_type);
field_value++;
int len = strlen(field_value);
time_value = (char *)malloc(sizeof(char)*len);
int index = 0;
while (field_value[index] != ',' && field_value[index] != '\r' && index < len)
{
time_value[index] = field_value[index];
index++;
}
time_value[index] = '\0';
time = (time_t)atol(time_value);
free(time_value);
return time;
}
void get_request_freshness(const char *value, struct request_freshness* restrict)
{
const char* field_value = NULL;
const char* cache_control_time[] = { "min-fresh", "max-age" };
int i = 0;
time_t relative_time;
field_value = strstr(value, "min-fresh");
if (field_value != NULL)
{
restrict->min_fresh = get_time_value(field_value, "min-fresh");;
}
field_value = strstr(value, "max-age");
if (field_value != NULL)
{
restrict->max_age = get_time_value(field_value, "max-age");;
}
}
enum cache_pending_action request_cache_control(const char* value, struct request_freshness* restrict)
{
int i = 0;
if (strstr(value, "no-cache") != NULL)
{
return VERIFY;
}
if (strstr(value, "no-store") != NULL)
{
return FORBIDDEN;
}
get_request_freshness(value, restrict);
return ALLOWED;
}
bool cache_vertify(const struct tfe_http_field *http_fields, size_t n_fields)
{
int i = 0;
for (; i < n_fields; i++)
{
if ( http_fields[i].http_field == TLF_HTTP_IF_MATCH ||
http_fields[i].http_field == TLF_HTTP_IF_NONE_MATCH ||
http_fields[i].http_field == TLF_HTTP_IF_MODIFIED_SINCE ||
http_fields[i].http_field == TLF_HTTP_IF_UNMODIFIED_SINCE )
{
return true;
}
}
return false;
}
const char* get_head_value(const struct tfe_http_field *http_fields, size_t n_fields, enum tfe_http_std_field head_key)
{
int i = 0;
for (i = 0; i < n_fields; i++)
{
if (http_fields[i].http_field == head_key)
{
return http_fields[i].value;
}
}
return NULL;
}
enum cache_pending_action get_pragma_action(const char * value)
{
const char *pragma_value = "no-cache";
if (memcmp(value, pragma_value, strlen(pragma_value)) == 0)
{
return VERIFY;
}
return UNDEFINED;
}
enum cache_pending_action tfe_cache_get_pending(const struct tfe_http_field *request, size_t n_fields, struct request_freshness* restrict)
{
enum cache_pending_action res = UNDEFINED;
int i = 0;
int index = 0;
const char *value = NULL;
memset(restrict,0,sizeof(struct request_freshness));
value = get_head_value(request, n_fields, TFE_HTTP_PRAGMA);
if (value != NULL)
{
res = get_pragma_action(value);
}
else
{
value = get_head_value(request, n_fields, TFE_HTTP_CACHE_CONTROL);
if (value != NULL)
{
res = request_cache_control(value, restrict);
}
else
{
if (cache_vertify(request, n_fields))
{
res = VERIFY;
}
}
}
return res;
}
time_t absolute_to_relative_time(const char* gmt_time)
{
time_t expire_rel_time;
struct tm expire_gmt_time;
strptime(gmt_time, "%a, %d %b %Y %H:%M:%S GMT", &expire_gmt_time);
expire_rel_time = mktime(&expire_gmt_time);
return expire_rel_time;
}
bool is_standard_gmt_format(const char* value)
{
int str_len = strlen(value);
if ((value[str_len - 1] == 'T') && (value[str_len - 2] == 'M') && (value[str_len - 3] == 'G'))
{
return true;
}
else
{
return false;
}
}
time_t get_relative_time(const char* value)
{
const char* temp_str = NULL;
char * str_age = NULL;
time_t time = 0;
const char * cache_ctl_time[] = { "s-maxage","max-age" };//s-maxage优先级大于max-age优先级
int i = 0;
for (; i < 2; i++)
{
temp_str = strstr(value, cache_ctl_time[i]);
}
return time;
}
time_t get_response_s_maxage(const char* cache_ctl)
{
const char* s_maxage = NULL;
s_maxage = strstr(cache_ctl, "s-maxage");
if (s_maxage != NULL)
{
return get_time_value(s_maxage, "s-maxage");
}
}
time_t get_response_maxage(const char* cache_ctl)
{
const char* max_age = NULL;
max_age = strstr(cache_ctl, "max-age");
if (max_age != NULL)
{
return get_time_value(max_age, "max-age");
}
}
void get_response_freshness(const struct tfe_http_field *response, size_t n_fields, struct response_freshness* freshness)
{
time_t expire_rel_time = 0;
time_t cur_rel_time = 0;
struct tm cur_gmt_time;
const char* field_value = NULL;
field_value = get_head_value(response, n_fields, TFE_HTTP_CACHE_CONTROL);
if (field_value != NULL)
{
freshness->timeout = get_response_s_maxage(field_value);
if (freshness->timeout == 0)
{
freshness->timeout = get_response_maxage(field_value);
}
}
else
{
field_value = get_head_value(response, n_fields, TFE_HTTP_EXPIRES);
if (field_value != NULL)
{
assert(is_standard_gmt_format(field_value));
expire_rel_time = absolute_to_relative_time(field_value);
const time_t cur_ct_time = time(NULL);
if (gmtime_r(&cur_ct_time, &cur_gmt_time) == NULL)
{
assert(0);
}
cur_rel_time = mktime(&cur_gmt_time);
freshness->timeout = expire_rel_time - cur_rel_time;
}
}
field_value = get_head_value(response, n_fields, TFE_HTTP_DATE);
if (field_value != NULL)
{
assert(is_standard_gmt_format(field_value));
freshness->date = absolute_to_relative_time(field_value);;
}
field_value = get_head_value(response, n_fields, TLF_HTTP_LAST_MODIFIED);
if (field_value != NULL)
{
assert(is_standard_gmt_format(field_value));
freshness->last_modified = absolute_to_relative_time(field_value);;
}
}
enum cache_pending_action response_cache_control(const char* value)
{
const char *forbidden_vaule[] = {"no-store", "private"};
const char *verify_vaule[] = { "no-cache", "must-revalidate","proxy-revalidate" };
int i = 0;
for (i = 0; i < 2; i++)
{
if (strstr(value, forbidden_vaule[i]) != NULL)
{
return FORBIDDEN;
}
}
for (i = 0; i < 3; i++)
{
if (strstr(value, verify_vaule[i]) != NULL)
{
return VERIFY;
}
}
return ALLOWED;
}
enum cache_pending_action tfe_cache_put_pending(const struct tfe_http_field *response, size_t n_fields, struct response_freshness* freshness)
{
enum cache_pending_action res = UNDEFINED;
int i = 0;
int index = 0;
const char *value = NULL;
memset(freshness,0,sizeof(struct response_freshness));
value = get_head_value(response, n_fields, TFE_HTTP_PRAGMA);
if (value != NULL)
{
res = get_pragma_action(value);
}
else
{
value = get_head_value(response, n_fields, TFE_HTTP_CACHE_CONTROL);
if (value != NULL)
{
res = response_cache_control(value);
}
else
{
value = get_head_value(response, n_fields, TFE_HTTP_EXPIRES);
if (value != NULL)
{
res = ALLOWED;
}
}
}
if (res == ALLOWED)
{
get_response_freshness(response, n_fields, freshness);
}
return res;
}
int main()
{
printf("please input the head info\n");
printf("first line input the head type and the num of head,0:request head 1:response head\n");
printf("next line input the head field,date:10,pragma :14,expires:23,cache-control:25,If-Match:26,If-None-Match:27,If-Modified-since:28,If-Unmodified-since:29,Last-Modified:30\n");
int head_type;
int head_num = 0;
scanf("%d %d", &head_type, &head_num);
struct tfe_http_field *http_heads = (struct tfe_http_field *)malloc(sizeof(struct tfe_http_field)*head_num);
printf("head type is %d and head_num is %d\n", head_type, head_num);
int i = 0;
for (; i < head_num; i++)
{
http_heads[i].value = (char *)malloc(sizeof(char) * 50);
scanf("%d ", &(http_heads[i].http_field));
fgets(http_heads[i].value, 50, stdin);
int len = strlen(http_heads[i].value);
http_heads[i].value[len - 1] = '\0';
printf("head field is %d and head value is %s\n", http_heads[i].http_field, http_heads[i].value);
}
enum cache_pending_action res;
struct request_freshness restrict;
struct response_freshness freshness;
switch (head_type)
{
case 0:
res = tfe_cache_get_pending(http_heads, head_num, &restrict);
printf(" request cache pending action is %d\n", res);
printf("request min-fresh is %lu,max-age=%lu\n", restrict.min_fresh, restrict.max_age);
break;
case 1:
res = tfe_cache_put_pending(http_heads, head_num, &freshness);
printf(" request cache pending action is %d\n", res);
printf("response date is %lu ,last-modified is %lu, timeout is %lu\n", freshness.date, freshness.last_modified, freshness.timeout);
break;
default:
assert(0);
break;
}
for (i = 0; i < head_num; i++)
{
free(http_heads[i].value);
}
free(http_heads);
return 0;
}

211
cache/test/test_cache_pending.cpp vendored Normal file
View File

@@ -0,0 +1,211 @@
#include <iostream>
#include <gtest/gtest.h>
#include <tango_cache_pending.h>
using namespace std;
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
TEST(CacheActionTest, PragmaField)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_PRAGMA;
http_heads.value = "no-cache";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1,&restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), VERIFY);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, CacheCtlNoCache)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "no-cache";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), VERIFY);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, CacheCtlNoStore)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "no-store";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), FORBIDDEN);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), FORBIDDEN);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, CacheCtlCached)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "only-if-cached, max-age=3600";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), ALLOWED);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 3600);
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), ALLOWED);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 3600);
}
TEST(CacheActionTest, CacheCtlMinFresh)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "only-if-cached, max-age=3600, min-fresh=60";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), ALLOWED);
EXPECT_EQ(restrict.min_fresh, 60);
EXPECT_EQ(restrict.max_age, 3600);
}
TEST(CacheActionTest, CacheCtlMustRevalidate)
{
struct tfe_http_field http_heads;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "must-revalidate";
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), VERIFY);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, CacheCtlProxyRevalidate)
{
struct tfe_http_field http_heads;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "proxy-revalidate";
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), VERIFY);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, CacheCtlCachedSMaxAge)
{
struct tfe_http_field http_heads;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "public, max-age=60, s-maxage=3600";
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), ALLOWED);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 3600);
}
TEST(CacheActionTest, CacheCtlPrivate)
{
struct tfe_http_field http_heads;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_CACHE_CONTROL;
http_heads.value = "private";
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), FORBIDDEN);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, ExpiresResponse)
{
struct tfe_http_field http_heads;
struct response_freshness freshness;
http_heads.http_field = TFE_HTTP_EXPIRES;
http_heads.value = "Sun, 01 Dec 2019 16:00:00 GMT";
EXPECT_EQ(tfe_cache_put_pending(&http_heads, 1, &freshness), ALLOWED);
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.last_modified, 0);
cout << freshness.timeout << endl;
}
TEST(CacheActionTest, IfMatchRequest)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
http_heads.http_field = TLF_HTTP_IF_MATCH;
http_heads.value = "50b1c1d4f775c61:df3";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
}
TEST(CacheActionTest, IfNoMatchRequest)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
http_heads.http_field = TLF_HTTP_IF_NONE_MATCH;
http_heads.value = "50b1c1d4f775c61:df3";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
}
TEST(CacheActionTest, IfModifiedSinceRequest)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
http_heads.http_field = TLF_HTTP_IF_MODIFIED_SINCE;
http_heads.value = "Sun, 01 Dec 2019 16:00:00 GMT";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
}
TEST(CacheActionTest, IfUnModifiedSinceRequest)
{
struct tfe_http_field http_heads;
struct request_freshness restrict;
http_heads.http_field = TLF_HTTP_IF_UNMODIFIED_SINCE;
http_heads.value = "Sun, 01 Dec 2019 16:00:00 GMT";
EXPECT_EQ(tfe_cache_get_pending(&http_heads, 1, &restrict), VERIFY);
EXPECT_EQ(restrict.min_fresh, 0);
EXPECT_EQ(restrict.max_age, 0);
}
TEST(CacheActionTest, Date)
{
struct tfe_http_field http_heads[2];
struct response_freshness freshness;
http_heads[0].http_field=TFE_HTTP_CACHE_CONTROL;
http_heads[0].value = "public, max=3600";
http_heads[1].http_field = TFE_HTTP_DATE;
http_heads[1].value = "Sun, 01 Dec 2019 16:00:00 GMT";
EXPECT_EQ(tfe_cache_put_pending(http_heads, 2, &freshness), ALLOWED);
cout << freshness.date << endl;
EXPECT_EQ(freshness.last_modified, 0);
EXPECT_EQ(freshness.timeout, 0);
}
TEST(CacheActionTest, LastModified)
{
struct tfe_http_field http_heads[2];
struct response_freshness freshness;
http_heads[0].http_field=TFE_HTTP_CACHE_CONTROL;
http_heads[0].value = "public, max=3600";
http_heads[1].http_field = TLF_HTTP_LAST_MODIFIED;
http_heads[1].value = "Sun, 01 Dec 2019 16:00:00 GMT";
EXPECT_EQ(tfe_cache_put_pending(http_heads, 2, &freshness),ALLOWED);
cout << freshness.last_modified << endl;
EXPECT_EQ(freshness.date, 0);
EXPECT_EQ(freshness.timeout, 0);
}