@@ -58,6 +58,7 @@ struct OptionInfo {
5858 bool disable_gzip_output{false };
5959 bool first_byte_flush{false };
6060 unsigned max_doc_size{1024 * 1024 };
61+ unsigned max_inclusion_depth{3 };
6162};
6263
6364static HandlerManager *gHandlerManager = nullptr ;
@@ -69,6 +70,9 @@ static Utils::HeaderValueList gAllowlistCookies;
6970#define MIME_FIELD_XESI " X-Esi"
7071#define MIME_FIELD_XESI_LEN 5
7172
73+ #define MIME_FIELD_XESIDEPTH " X-Esi-Depth"
74+ #define MIME_FIELD_XESIDEPTH_LEN 11
75+
7276#define HTTP_VALUE_PRIVATE_EXPIRES " -1"
7377#define HTTP_VALUE_PRIVATE_CC " max-age=0, private"
7478
@@ -298,46 +302,66 @@ ContData::getClientState()
298302 }
299303 TSHandleMLocRelease (bufp, req_hdr_loc, url_loc);
300304 }
301- TSMLoc field_loc = TSMimeHdrFieldGet (req_bufp, req_hdr_loc, 0 );
305+
306+ TSMLoc field_loc = TSMimeHdrFieldGet (req_bufp, req_hdr_loc, 0 );
307+ bool depth_field = false ;
302308 while (field_loc) {
303309 TSMLoc next_field_loc;
304310 const char *name;
305311 int name_len;
306312
307313 name = TSMimeHdrFieldNameGet (req_bufp, req_hdr_loc, field_loc, &name_len);
308314 if (name) {
309- int n_values;
310- n_values = TSMimeHdrFieldValuesCount (req_bufp, req_hdr_loc, field_loc);
311- if (n_values && (n_values != TS_ERROR)) {
312- const char *value = nullptr ;
313- int value_len = 0 ;
314- if (n_values == 1 ) {
315- value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, 0 , &value_len);
316-
317- if (nullptr != value && value_len) {
318- if (Utils::areEqual (name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING) &&
319- Utils::areEqual (value, value_len, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
320- gzip_output = true ;
321- }
322- }
315+ if (Utils::areEqual (name, name_len, MIME_FIELD_XESIDEPTH, MIME_FIELD_XESIDEPTH_LEN)) {
316+ unsigned d = TSMimeHdrFieldValueUintGet (req_bufp, req_hdr_loc, field_loc, -1 );
317+ d = (d + 1 ) % 10 ;
318+ char dstr[2 ];
319+ int const len = snprintf (dstr, sizeof (dstr), " %u" , d);
320+
321+ HttpHeader header;
322+ if (len != 1 ) {
323+ header = HttpHeader (MIME_FIELD_XESIDEPTH, MIME_FIELD_XESIDEPTH_LEN, " 1" , 1 );
323324 } else {
324- for (int i = 0 ; i < n_values; ++i) {
325- value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, i, &value_len);
325+ header = HttpHeader (MIME_FIELD_XESIDEPTH, MIME_FIELD_XESIDEPTH_LEN, dstr, 1 );
326+ }
327+ data_fetcher->useHeader (header);
328+ esi_vars->populate (header);
329+ depth_field = true ;
330+
331+ } else {
332+ int n_values;
333+ n_values = TSMimeHdrFieldValuesCount (req_bufp, req_hdr_loc, field_loc);
334+ if (n_values && (n_values != TS_ERROR)) {
335+ const char *value = nullptr ;
336+ int value_len = 0 ;
337+ if (n_values == 1 ) {
338+ value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, 0 , &value_len);
339+
326340 if (nullptr != value && value_len) {
327341 if (Utils::areEqual (name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING) &&
328342 Utils::areEqual (value, value_len, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
329343 gzip_output = true ;
330344 }
331345 }
332- }
346+ } else {
347+ for (int i = 0 ; i < n_values; ++i) {
348+ value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, i, &value_len);
349+ if (nullptr != value && value_len) {
350+ if (Utils::areEqual (name, name_len, TS_MIME_FIELD_ACCEPT_ENCODING, TS_MIME_LEN_ACCEPT_ENCODING) &&
351+ Utils::areEqual (value, value_len, TS_HTTP_VALUE_GZIP, TS_HTTP_LEN_GZIP)) {
352+ gzip_output = true ;
353+ }
354+ }
355+ }
333356
334- value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, -1 , &value_len);
335- }
357+ value = TSMimeHdrFieldValueStringGet (req_bufp, req_hdr_loc, field_loc, -1 , &value_len);
358+ }
336359
337- if (value != nullptr ) {
338- HttpHeader header (name, name_len, value, value_len);
339- data_fetcher->useHeader (header);
340- esi_vars->populate (header);
360+ if (value != nullptr ) {
361+ HttpHeader header (name, name_len, value, value_len);
362+ data_fetcher->useHeader (header);
363+ esi_vars->populate (header);
364+ }
341365 }
342366 }
343367 }
@@ -346,6 +370,12 @@ ContData::getClientState()
346370 TSHandleMLocRelease (req_bufp, req_hdr_loc, field_loc);
347371 field_loc = next_field_loc;
348372 }
373+
374+ if (depth_field == false ) {
375+ HttpHeader header (MIME_FIELD_XESIDEPTH, MIME_FIELD_XESIDEPTH_LEN, " 1" , 1 );
376+ data_fetcher->useHeader (header);
377+ esi_vars->populate (header);
378+ }
349379 }
350380
351381 if (gzip_output) {
@@ -1229,7 +1259,7 @@ maskOsCacheHeaders(TSHttpTxn txnp)
12291259}
12301260
12311261static bool
1232- isTxnTransformable (TSHttpTxn txnp, bool is_cache_txn, bool *intercept_header, bool *head_only)
1262+ isTxnTransformable (TSHttpTxn txnp, bool is_cache_txn, const OptionInfo *pOptionInfo, bool *intercept_header, bool *head_only)
12331263{
12341264 // We are only interested in transforming "200 OK" responses with a
12351265 // Content-Type: text/ header and with X-Esi header
@@ -1244,6 +1274,21 @@ isTxnTransformable(TSHttpTxn txnp, bool is_cache_txn, bool *intercept_header, bo
12441274 return false ;
12451275 }
12461276
1277+ TSMLoc loc;
1278+ unsigned d;
1279+
1280+ d = 0 ;
1281+ loc = TSMimeHdrFieldFind (bufp, hdr_loc, MIME_FIELD_XESIDEPTH, MIME_FIELD_XESIDEPTH_LEN);
1282+ if (loc != TS_NULL_MLOC) {
1283+ d = TSMimeHdrFieldValueUintGet (bufp, hdr_loc, loc, -1 );
1284+ }
1285+ TSHandleMLocRelease (bufp, hdr_loc, loc);
1286+ if (d >= pOptionInfo->max_inclusion_depth ) {
1287+ TSError (" [esi][%s] The current esi inclusion depth (%u) is larger than or equal to the max (%u)" , __FUNCTION__, d,
1288+ pOptionInfo->max_inclusion_depth );
1289+ return false ;
1290+ }
1291+
12471292 int method_len;
12481293 const char *method;
12491294 method = TSHttpHdrMethodGet (bufp, hdr_loc, &method_len);
@@ -1318,7 +1363,7 @@ isTxnTransformable(TSHttpTxn txnp, bool is_cache_txn, bool *intercept_header, bo
13181363}
13191364
13201365static bool
1321- isCacheObjTransformable (TSHttpTxn txnp, bool *intercept_header, bool *head_only)
1366+ isCacheObjTransformable (TSHttpTxn txnp, const OptionInfo *pOptionInfo, bool *intercept_header, bool *head_only)
13221367{
13231368 int obj_status;
13241369 if (TSHttpTxnCacheLookupStatusGet (txnp, &obj_status) == TS_ERROR) {
@@ -1327,7 +1372,7 @@ isCacheObjTransformable(TSHttpTxn txnp, bool *intercept_header, bool *head_only)
13271372 }
13281373 if (obj_status == TS_CACHE_LOOKUP_HIT_FRESH) {
13291374 Dbg (dbg_ctl_local, " [%s] doc found in cache, will add transformation" , __FUNCTION__);
1330- return isTxnTransformable (txnp, true , intercept_header, head_only);
1375+ return isTxnTransformable (txnp, true , pOptionInfo, intercept_header, head_only);
13311376 }
13321377 Dbg (dbg_ctl_local, " [%s] cache object's status is %d; not transformable" , __FUNCTION__, obj_status);
13331378 return false ;
@@ -1499,7 +1544,7 @@ globalHookHandler(TSCont contp, TSEvent event, void *edata)
14991544 if (event == TS_EVENT_HTTP_READ_RESPONSE_HDR) {
15001545 bool mask_cache_headers = false ;
15011546 Dbg (dbg_ctl_local, " [%s] handling read response header event" , __FUNCTION__);
1502- if (isTxnTransformable (txnp, false , &intercept_header, &head_only)) {
1547+ if (isTxnTransformable (txnp, false , pOptionInfo, &intercept_header, &head_only)) {
15031548 addTransform (txnp, true , intercept_header, head_only, pOptionInfo);
15041549 Stats::increment (Stats::N_OS_DOCS);
15051550 mask_cache_headers = true ;
@@ -1512,7 +1557,7 @@ globalHookHandler(TSCont contp, TSEvent event, void *edata)
15121557 }
15131558 } else {
15141559 Dbg (dbg_ctl_local, " [%s] handling cache lookup complete event" , __FUNCTION__);
1515- if (isCacheObjTransformable (txnp, &intercept_header, &head_only)) {
1560+ if (isCacheObjTransformable (txnp, pOptionInfo, &intercept_header, &head_only)) {
15161561 // we make the assumption above that a transformable cache
15171562 // object would already have a transformation. We should revisit
15181563 // that assumption in case we change the statement below
@@ -1575,11 +1620,12 @@ esiPluginInit(int argc, const char *argv[], OptionInfo *pOptionInfo)
15751620 {const_cast <char *>(" first-byte-flush" ), no_argument, nullptr , ' b' },
15761621 {const_cast <char *>(" handler-filename" ), required_argument, nullptr , ' f' },
15771622 {const_cast <char *>(" max-doc-size" ), required_argument, nullptr , ' d' },
1623+ {const_cast <char *>(" max-inclusion-depth" ), required_argument, nullptr , ' i' },
15781624 {nullptr , 0 , nullptr , 0 },
15791625 };
15801626
15811627 int longindex = 0 ;
1582- while ((c = getopt_long (argc, const_cast <char *const *>(argv), " npzbf:d:" , longopts, &longindex)) != -1 ) {
1628+ while ((c = getopt_long (argc, const_cast <char *const *>(argv), " npzbf:d:i: " , longopts, &longindex)) != -1 ) {
15831629 switch (c) {
15841630 case ' n' :
15851631 pOptionInfo->packed_node_support = true ;
@@ -1621,6 +1667,18 @@ esiPluginInit(int argc, const char *argv[], OptionInfo *pOptionInfo)
16211667 pOptionInfo->max_doc_size = max * coeff;
16221668 break ;
16231669 }
1670+ case ' i' : {
1671+ unsigned max;
1672+ auto num = std::sscanf (optarg, " %u" , &max);
1673+ if (num != 1 ) {
1674+ TSEmergency (" [esi][%s] value for maximum inclusion depth (%s) is not unsigned integer" , __FUNCTION__, optarg);
1675+ }
1676+ if (max > 9 ) {
1677+ TSEmergency (" [esi][%s] maximum inclusion depth (%s) large than 9" , __FUNCTION__, optarg);
1678+ }
1679+ pOptionInfo->max_inclusion_depth = max;
1680+ break ;
1681+ }
16241682 default :
16251683 TSEmergency (" [esi][%s] bad option" , __FUNCTION__);
16261684 return -1 ;
@@ -1630,9 +1688,10 @@ esiPluginInit(int argc, const char *argv[], OptionInfo *pOptionInfo)
16301688
16311689 Dbg (dbg_ctl_local,
16321690 " [%s] Plugin started, "
1633- " packed-node-support: %d, private-response: %d, disable-gzip-output: %d, first-byte-flush: %d, max-doc-size %u " ,
1691+ " packed-node-support: %d, private-response: %d, disable-gzip-output: %d, first-byte-flush: %d, max-doc-size %u, "
1692+ " max-inclusion-depth %u " ,
16341693 __FUNCTION__, pOptionInfo->packed_node_support , pOptionInfo->private_response , pOptionInfo->disable_gzip_output ,
1635- pOptionInfo->first_byte_flush , pOptionInfo->max_doc_size );
1694+ pOptionInfo->first_byte_flush , pOptionInfo->max_doc_size , pOptionInfo-> max_inclusion_depth );
16361695
16371696 return 0 ;
16381697}
0 commit comments