diff --git a/cache/include/tango_cache_pending.h b/cache/include/tango_cache_pending.h new file mode 100644 index 0000000..a8d5858 --- /dev/null +++ b/cache/include/tango_cache_pending.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +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); \ No newline at end of file diff --git a/cache/src/tango_cache_pending.cpp b/cache/src/tango_cache_pending.cpp new file mode 100644 index 0000000..9d82c31 --- /dev/null +++ b/cache/src/tango_cache_pending.cpp @@ -0,0 +1,352 @@ +#include"proxy_cache.h" +#include +#include +#include +#include +#include + + +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; +} diff --git a/cache/test/test_cache_pending.cpp b/cache/test/test_cache_pending.cpp new file mode 100644 index 0000000..447937a --- /dev/null +++ b/cache/test/test_cache_pending.cpp @@ -0,0 +1,211 @@ +#include +#include +#include + +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); +}