Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse "pragma" field in response, honor "pragma" field in request #1120

Merged
merged 3 commits into from
Nov 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tempesta_fw/cache.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,9 @@ tfw_cache_employ_resp(TfwHttpResp *resp)
return false;
if (resp->cache_ctl.flags & CC_RESP_DONTCACHE)
return false;
if (!(req->cache_ctl.flags & TFW_HTTP_CC_IS_PRESENT)
&& (req->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE))
return false;
if (!(resp->cache_ctl.flags & TFW_HTTP_CC_IS_PRESENT)
&& (resp->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE))
return false;
Expand Down
71 changes: 52 additions & 19 deletions tempesta_fw/http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,10 @@ enum {

I_EoT, /* end of term */
I_EoL,

/* Pragma header */
I_Pragma,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big deal, just seems like End of Term and End of Line should be last in the enumeration and Pragma header enum values should stand closer to other headers enums. Look a little bit messy as for me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made follow-up changes in #1124.

I_Pragma_Ext,
};

/* Initialize TRY_STR parsing context */
Expand Down Expand Up @@ -1813,6 +1817,13 @@ enum {
Resp_HdrLast_Modifie,
Resp_HdrLast_Modified,
Resp_HdrLast_ModifiedV,
Resp_HdrP,
Resp_HdrPr,
Resp_HdrPra,
Resp_HdrPrag,
Resp_HdrPragm,
Resp_HdrPragma,
Resp_HdrPragmaV,
Resp_HdrS,
Resp_HdrSe,
Resp_HdrSer,
Expand Down Expand Up @@ -1874,9 +1885,6 @@ enum {
Req_I_CC_MaxStale,
Req_I_CC_MaxStaleV,
Req_I_CC_Ext,
/* Pragma header */
Req_I_Pragma,
Req_I_Pragma_Ext,
/* X-Forwarded-For header */
Req_I_XFF,
Req_I_XFF_Node_Id,
Expand Down Expand Up @@ -2752,43 +2760,45 @@ __req_parse_if_msince(TfwHttpMsg *msg, unsigned char *data, size_t len)
}

/**
* Parse request Pragma header field, RFC 7234 5.4.
* The meaning of "Pragma: no-cache" in responses is not specified.
* Parse Pragma header field. Request semantics is described in RFC 7234 5.4.
* The meaning of "Pragma: no-cache" in responses is not specified. However,
* some applications may expect it to prevent caching being in responses as
* well.
*/
static int
__req_parse_pragma(TfwHttpReq *req, unsigned char *data, size_t len)
__parse_pragma(TfwHttpMsg *hm, unsigned char *data, size_t len)
{
int r = CSTR_NEQ;
__FSM_DECLARE_VARS(req);
__FSM_DECLARE_VARS(hm);

__FSM_START(parser->_i_st) {

__FSM_STATE(Req_I_Pragma) {
__FSM_STATE(I_Pragma) {
TRY_STR_LAMBDA("no-cache", {
req->cache_ctl.flags |= TFW_HTTP_CC_PRAGMA_NO_CACHE;
}, Req_I_Pragma_Ext);
msg->cache_ctl.flags |= TFW_HTTP_CC_PRAGMA_NO_CACHE;
}, I_Pragma_Ext);
TRY_STR_INIT();
__FSM_I_MOVE_n(Req_I_Pragma_Ext, 0);
__FSM_I_MOVE_n(I_Pragma_Ext, 0);
}

__FSM_STATE(Req_I_Pragma_Ext) {
__FSM_STATE(I_Pragma_Ext) {
/* Verify and just skip the extensions. */
__FSM_I_MATCH_MOVE(qetoken, Req_I_Pragma_Ext);
__FSM_I_MATCH_MOVE(qetoken, I_Pragma_Ext);
c = *(p + __fsm_sz);
if (IS_WS(c) || c == ',')
__FSM_I_MOVE_n(Req_I_EoT, __fsm_sz + 1);
__FSM_I_MOVE_n(I_EoT, __fsm_sz + 1);
if (IS_CRLF(c))
return __data_off(p + __fsm_sz);
return CSTR_NEQ;
}

/* End of term. */
__FSM_STATE(Req_I_EoT) {
__FSM_STATE(I_EoT) {
if (IS_WS(c) || c == ',')
__FSM_I_MOVE(Req_I_EoT);
__FSM_I_MOVE(I_EoT);
if (IS_CRLF(c))
return __data_off(p);
__FSM_I_MOVE_n(Req_I_Pragma_Ext, 0);
__FSM_I_MOVE_n(I_Pragma_Ext, 0);
}

} /* FSM END */
Expand Down Expand Up @@ -3671,8 +3681,8 @@ tfw_http_parse_req(void *req_data, unsigned char *data, size_t len)
__parse_keep_alive, TFW_HTTP_HDR_KEEP_ALIVE);

/* 'Pragma:*OWS' is read, process field-value. */
TFW_HTTP_PARSE_RAWHDR_VAL(Req_HdrPragmaV, Req_I_Pragma,
req, __req_parse_pragma);
TFW_HTTP_PARSE_RAWHDR_VAL(Req_HdrPragmaV, I_Pragma, msg,
__parse_pragma);

/* 'Referer:*OWS' is read, process field-value. */
TFW_HTTP_PARSE_SPECHDR_VAL(Req_HdrRefererV, Req_I_Referer, msg,
Expand Down Expand Up @@ -4648,6 +4658,17 @@ tfw_http_parse_resp(void *resp_data, unsigned char *data, size_t len)
}
__FSM_MOVE(Resp_HdrL);

case 'p':
if (likely(__data_available(p, 7))
&& C4_INT_LCM(p + 1, 'r', 'a', 'g', 'm')
&& TFW_LC(*(p + 5)) == 'a'
&& *(p + 6) == ':')
{
parser->_i_st = Resp_HdrPragmaV;
__FSM_MOVE_n(RGen_OWS, 7);
}
__FSM_MOVE(Resp_HdrP);

case 's':
if (likely(__data_available(p, 7)
&& C4_INT_LCM(p + 1, 'e', 'r', 'v', 'e')
Expand Down Expand Up @@ -4743,6 +4764,10 @@ tfw_http_parse_resp(void *resp_data, unsigned char *data, size_t len)
TFW_HTTP_PARSE_RAWHDR_VAL(Resp_HdrLast_ModifiedV, I_Date, msg,
__parse_http_date);

/* 'Pragma:*OWS' is read, process field-value. */
TFW_HTTP_PARSE_RAWHDR_VAL(Resp_HdrPragmaV, I_Pragma, msg,
__parse_pragma);

/* 'Server:*OWS' is read, process field-value. */
TFW_HTTP_PARSE_SPECHDR_VAL(Resp_HdrServerV, Resp_I_Server, resp,
__resp_parse_server, TFW_HTTP_HDR_SERVER);
Expand Down Expand Up @@ -4912,6 +4937,14 @@ tfw_http_parse_resp(void *resp_data, unsigned char *data, size_t len)
__FSM_TX_AF(Resp_HdrLast_Modifie, 'd', Resp_HdrLast_Modified, RGen_HdrOther);
__FSM_TX_AF_OWS(Resp_HdrLast_Modified, ':', Resp_HdrLast_ModifiedV, RGen_HdrOther);

/* Pragma header processing. */
__FSM_TX_AF(Resp_HdrP, 'r', Resp_HdrPr, RGen_HdrOther);
__FSM_TX_AF(Resp_HdrPr, 'a', Resp_HdrPra, RGen_HdrOther);
__FSM_TX_AF(Resp_HdrPra, 'g', Resp_HdrPrag, RGen_HdrOther);
__FSM_TX_AF(Resp_HdrPrag, 'm', Resp_HdrPragm, RGen_HdrOther);
__FSM_TX_AF(Resp_HdrPragm, 'a', Resp_HdrPragma, RGen_HdrOther);
__FSM_TX_AF_OWS(Resp_HdrPragma, ':', Resp_HdrPragmaV, RGen_HdrOther);

/* Server header processing. */
__FSM_TX_AF(Resp_HdrS, 'e', Resp_HdrSe, RGen_HdrOther);
__FSM_TX_AF(Resp_HdrSe, 'r', Resp_HdrSer, RGen_HdrOther);
Expand Down
57 changes: 57 additions & 0 deletions tempesta_fw/t/unit/test_http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,62 @@ TEST(http_parser, fills_hdr_tbl_for_resp)
}
}

TEST(http_parser, cache_control_flags)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New tests are only about parsing responses, requests are not checked, not all TFW_HTTP_CC_* flags are tested. No need to address this comment in the PR, since functional tests from tempesta-tech/tempesta-test#59 will cover the some cases. Just wanted to link this test case with #1059 issue.

{
FOR_RESP("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"Cache-Control: max-age=5, private, no-cache, ext=foo\r\n"
"\r\n");
{
EXPECT_TRUE(resp->cache_ctl.flags & TFW_HTTP_CC_PRIVATE);
EXPECT_TRUE(resp->cache_ctl.flags & TFW_HTTP_CC_NO_CACHE);
EXPECT_TRUE(resp->cache_ctl.flags & TFW_HTTP_CC_MAX_AGE);
EXPECT_FALSE(
resp->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE);
}

FOR_RESP("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"Pragma: no-cache\r\n"
"\r\n");
{
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_PRIVATE);
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_NO_CACHE);
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_MAX_AGE);
EXPECT_TRUE(
resp->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE);
}

FOR_RESP("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"Connection: Keep-Alive\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"\r\n");
{
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_PRIVATE);
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_NO_CACHE);
EXPECT_FALSE(resp->cache_ctl.flags & TFW_HTTP_CC_MAX_AGE);
EXPECT_FALSE(
resp->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE);
}

FOR_RESP("HTTP/1.1 200 OK\r\n"
"Content-Length: 0\r\n"
"Connection: Keep-Alive\r\n"
"Pragma: nocache\r\n"
"Content-Type: text/html; charset=iso-8859-1\r\n"
"\r\n");
{
/* Contents of "Pragma" is not "no-cache" exactly. */
EXPECT_FALSE(
resp->cache_ctl.flags & TFW_HTTP_CC_PRAGMA_NO_CACHE);
}
}

TEST(http_parser, suspicious_x_forwarded_for)
{
FOR_REQ("GET / HTTP/1.1\r\n"
Expand Down Expand Up @@ -2237,6 +2293,7 @@ TEST_SUITE(http_parser)
TEST_RUN(http_parser, alphabets);
TEST_RUN(http_parser, fills_hdr_tbl_for_req);
TEST_RUN(http_parser, fills_hdr_tbl_for_resp);
TEST_RUN(http_parser, cache_control_flags);
TEST_RUN(http_parser, suspicious_x_forwarded_for);
TEST_RUN(http_parser, parses_connection_value);
TEST_RUN(http_parser, content_length);
Expand Down