Skip to content

Commit ffbcfee

Browse files
authored
Require the use of CRLF in chunked message body (#12149)
* Require the use of CRLF in chunked message body * Fix docs
1 parent e948b7b commit ffbcfee

16 files changed

Lines changed: 153 additions & 42 deletions

File tree

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,15 @@ allow-plain
10281028
for details about chunked trailers. By default, this option is enabled
10291029
and therefore |TS| will drop chunked trailers.
10301030

1031+
.. ts:cv:: CONFIG proxy.config.http.strict_chunk_parsing INT 1
1032+
:reloadable:
1033+
:overridable:
1034+
1035+
Specifies whether |TS| strictly checks errors in chunked message body.
1036+
If enabled (``1``), |TS| returns 400 Bad Request if chunked message body is
1037+
not compliant with RFC 9112. If disabled (``0``), |TS| allows using LF as
1038+
a line terminator.
1039+
10311040
.. ts:cv:: CONFIG proxy.config.http.send_http11_requests INT 1
10321041
:reloadable:
10331042
:overridable:

doc/developer-guide/api/functions/TSHttpOverridableConfig.en.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ TSOverridableConfigKey Value Config
112112
:enumerator:`TS_CONFIG_HTTP_CACHE_WHEN_TO_REVALIDATE` :ts:cv:`proxy.config.http.cache.when_to_revalidate`
113113
:enumerator:`TS_CONFIG_HTTP_CHUNKING_ENABLED` :ts:cv:`proxy.config.http.chunking_enabled`
114114
:enumerator:`TS_CONFIG_HTTP_CHUNKING_SIZE` :ts:cv:`proxy.config.http.chunking.size`
115+
:enumerator:`TS_CONFIG_HTTP_STRICT_CHUNK_PARSING` :ts:cv:`proxy.config.http.strict_chunk_parsing`
115116
:enumerator:`TS_CONFIG_HTTP_DROP_CHUNKED_TRAILERS` :ts:cv:`proxy.config.http.drop_chunked_trailers`
116117
:enumerator:`TS_CONFIG_HTTP_CONNECT_ATTEMPTS_MAX_RETRIES_DOWN_SERVER` :ts:cv:`proxy.config.http.connect_attempts_max_retries_down_server`
117118
:enumerator:`TS_CONFIG_HTTP_CONNECT_ATTEMPTS_MAX_RETRIES` :ts:cv:`proxy.config.http.connect_attempts_max_retries`

doc/developer-guide/api/types/TSOverridableConfigKey.en.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Enumeration Members
9191
.. enumerator:: TS_CONFIG_NET_SOCK_PACKET_TOS_OUT
9292
.. enumerator:: TS_CONFIG_HTTP_INSERT_AGE_IN_RESPONSE
9393
.. enumerator:: TS_CONFIG_HTTP_CHUNKING_SIZE
94+
.. enumerator:: TS_CONFIG_HTTP_STRICT_CHUNK_PARSING
9495
.. enumerator:: TS_CONFIG_HTTP_DROP_CHUNKED_TRAILERS
9596
.. enumerator:: TS_CONFIG_HTTP_FLOW_CONTROL_ENABLED
9697
.. enumerator:: TS_CONFIG_HTTP_FLOW_CONTROL_LOW_WATER_MARK

include/proxy/http/HttpConfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ struct OverridableHttpConfigParams {
655655

656656
MgmtInt http_chunking_size = 4096; ///< Maximum chunk size for chunked output.
657657
MgmtByte http_drop_chunked_trailers = 1; ///< Whether to drop chunked trailers.
658+
MgmtByte http_strict_chunk_parsing = 1; ///< Whether to parse chunked body strictly.
658659
MgmtInt flow_high_water_mark = 0; ///< Flow control high water mark.
659660
MgmtInt flow_low_water_mark = 0; ///< Flow control low water mark.
660661

include/proxy/http/HttpTunnel.h

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ struct ChunkedHandler {
111111
*/
112112
bool drop_chunked_trailers = false;
113113

114+
bool strict_chunk_parsing = true;
115+
114116
bool truncation = false;
115117

116118
/** The number of bytes to skip from the reader because they are not body bytes.
@@ -127,8 +129,10 @@ struct ChunkedHandler {
127129
int last_server_event = VC_EVENT_NONE;
128130

129131
// Chunked header size parsing info.
130-
int running_sum = 0;
131-
int num_digits = 0;
132+
int running_sum = 0;
133+
int num_digits = 0;
134+
int num_cr = 0;
135+
bool prev_is_cr = false;
132136

133137
/// @name Output data.
134138
//@{
@@ -143,8 +147,8 @@ struct ChunkedHandler {
143147
//@}
144148
ChunkedHandler();
145149

146-
void init(IOBufferReader *buffer_in, HttpTunnelProducer *p, bool drop_chunked_trailers);
147-
void init_by_action(IOBufferReader *buffer_in, Action action, bool drop_chunked_trailers);
150+
void init(IOBufferReader *buffer_in, HttpTunnelProducer *p, bool drop_chunked_trailers, bool strict_parsing);
151+
void init_by_action(IOBufferReader *buffer_in, Action action, bool drop_chunked_trailers, bool strict_parsing);
148152
void clear();
149153

150154
/// Set the max chunk @a size.
@@ -389,6 +393,7 @@ class HttpTunnel : public Continuation
389393

390394
/// A named variable for the @a drop_chunked_trailers parameter to @a set_producer_chunking_action.
391395
static constexpr bool DROP_CHUNKED_TRAILERS = true;
396+
static constexpr bool PARSE_CHUNK_STRICTLY = true;
392397

393398
/** Designate chunking behavior to the producer.
394399
*
@@ -399,9 +404,10 @@ class HttpTunnel : public Continuation
399404
* @param[in] drop_chunked_trailers If @c true, chunked trailers are filtered
400405
* out. Logically speaking, this is only applicable when proxying chunked
401406
* content, thus only when @a action is @c TCA_PASSTHRU_CHUNKED_CONTENT.
407+
* @param[in] parse_chunk_strictly If @c true, no parse error will be allowed
402408
*/
403409
void set_producer_chunking_action(HttpTunnelProducer *p, int64_t skip_bytes, TunnelChunkingAction_t action,
404-
bool drop_chunked_trailers);
410+
bool drop_chunked_trailers, bool parse_chunk_strictly);
405411
/// Set the maximum (preferred) chunk @a size of chunked output for @a producer.
406412
void set_producer_chunking_size(HttpTunnelProducer *producer, int64_t size);
407413

@@ -480,6 +486,9 @@ class HttpTunnel : public Continuation
480486
/// Corresponds to proxy.config.http.drop_chunked_trailers having a value of 1.
481487
bool http_drop_chunked_trailers = false;
482488

489+
/// Corresponds to proxy.config.http.strict_chunk_parsing having a value of 1.
490+
bool http_strict_chunk_parsing = false;
491+
483492
/** The number of body bytes processed in this last execution of the tunnel.
484493
*
485494
* This accounting is used to determine how many bytes to copy into the body

include/ts/apidefs.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ enum TSOverridableConfigKey {
893893
TS_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT,
894894
TS_CONFIG_HTTP_CACHE_IGNORE_QUERY,
895895
TS_CONFIG_HTTP_DROP_CHUNKED_TRAILERS,
896+
TS_CONFIG_HTTP_STRICT_CHUNK_PARSING,
896897
TS_CONFIG_LAST_ENTRY,
897898
};
898899

plugins/lua/ts_lua_http_config.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ typedef enum {
149149
TS_LUA_CONFIG_NET_DEFAULT_INACTIVITY_TIMEOUT = TS_CONFIG_NET_DEFAULT_INACTIVITY_TIMEOUT,
150150
TS_LUA_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT = TS_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT,
151151
TS_LUA_CONFIG_HTTP_CACHE_IGNORE_QUERY = TS_CONFIG_HTTP_CACHE_IGNORE_QUERY,
152+
TS_LUA_CONFIG_HTTP_STRICT_CHUNK_PARSING = TS_CONFIG_HTTP_STRICT_CHUNK_PARSING,
152153
TS_LUA_CONFIG_LAST_ENTRY = TS_CONFIG_LAST_ENTRY,
153154
} TSLuaOverridableConfigKey;
154155

@@ -289,6 +290,7 @@ ts_lua_var_item ts_lua_http_config_vars[] = {
289290
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_NET_DEFAULT_INACTIVITY_TIMEOUT),
290291
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_NO_DNS_JUST_FORWARD_TO_PARENT),
291292
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_CACHE_IGNORE_QUERY),
293+
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_STRICT_CHUNK_PARSING),
292294
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_LAST_ENTRY),
293295
};
294296

src/api/InkAPI.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7136,6 +7136,9 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overr
71367136
case TS_CONFIG_HTTP_DROP_CHUNKED_TRAILERS:
71377137
ret = _memberp_to_generic(&overridableHttpConfig->http_drop_chunked_trailers, conv);
71387138
break;
7139+
case TS_CONFIG_HTTP_STRICT_CHUNK_PARSING:
7140+
ret = _memberp_to_generic(&overridableHttpConfig->http_strict_chunk_parsing, conv);
7141+
break;
71397142
case TS_CONFIG_HTTP_FLOW_CONTROL_ENABLED:
71407143
ret = _memberp_to_generic(&overridableHttpConfig->flow_control_enabled, conv);
71417144
break;

src/api/InkAPITest.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8715,7 +8715,8 @@ std::array<std::string_view, TS_CONFIG_LAST_ENTRY> SDK_Overridable_Configs = {
87158715
"proxy.config.body_factory.response_suppression_mode", "proxy.config.http.parent_proxy.enable_parent_timeout_markdowns",
87168716
"proxy.config.http.parent_proxy.disable_parent_markdowns", "proxy.config.net.default_inactivity_timeout",
87178717
"proxy.config.http.no_dns_just_forward_to_parent", "proxy.config.http.cache.ignore_query",
8718-
"proxy.config.http.drop_chunked_trailers", }
8718+
"proxy.config.http.drop_chunked_trailers", "proxy.config.http.strict_chunk_parsing",
8719+
}
87198720
};
87208721

87218722
extern ClassAllocator<HttpSM> httpSMAllocator;

src/proxy/FetchSM.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ FetchSM::check_chunked()
228228

229229
if (resp_is_chunked && (fetch_flags & TS_FETCH_FLAGS_DECHUNK)) {
230230
ChunkedHandler *ch = &chunked_handler;
231-
ch->init_by_action(resp_reader, ChunkedHandler::ACTION_DECHUNK, HttpTunnel::DROP_CHUNKED_TRAILERS);
231+
ch->init_by_action(resp_reader, ChunkedHandler::ACTION_DECHUNK, HttpTunnel::DROP_CHUNKED_TRAILERS,
232+
HttpTunnel::PARSE_CHUNK_STRICTLY);
232233
ch->dechunked_reader = ch->dechunked_buffer->alloc_reader();
233234
ch->state = ChunkedHandler::CHUNK_READ_SIZE;
234235
resp_reader->dealloc();

0 commit comments

Comments
 (0)