Skip to content

Commit 10681dc

Browse files
author
Zizhong Zhang
committed
TS-4042: Add feature to buffer request body before making downstream requests
1 parent 91f1bb7 commit 10681dc

File tree

16 files changed

+408
-137
lines changed

16 files changed

+408
-137
lines changed

example/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ example_Plugins = \
3232
blacklist_0.la \
3333
blacklist_1.la \
3434
bnull_transform.la \
35+
request_buffer.la \
3536
cache_scan.la \
3637
file_1.la \
3738
hello.la \
@@ -98,6 +99,7 @@ basic_auth_la_SOURCES = basic_auth/basic_auth.c
9899
blacklist_0_la_SOURCES = blacklist_0/blacklist_0.c
99100
blacklist_1_la_SOURCES = blacklist_1/blacklist_1.c
100101
bnull_transform_la_SOURCES = bnull_transform/bnull_transform.c
102+
request_buffer_la_SOURCES = request_buffer/request_buffer.c
101103
cache_scan_la_SOURCES = cache_scan/cache_scan.cc
102104
file_1_la_SOURCES = file_1/file_1.c
103105
hello_la_SOURCES = hello/hello.c
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/** @file
2+
3+
A brief file description
4+
5+
@section license License
6+
7+
Licensed to the Apache Software Foundation (ASF) under one
8+
or more contributor license agreements. See the NOTICE file
9+
distributed with this work for additional information
10+
regarding copyright ownership. The ASF licenses this file
11+
to you under the Apache License, Version 2.0 (the
12+
"License"); you may not use this file except in compliance
13+
with the License. You may obtain a copy of the License at
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
See the License for the specific language governing permissions and
21+
limitations under the License.
22+
*/
23+
24+
#include <stdio.h>
25+
#include <stdlib.h>
26+
#include <string.h>
27+
28+
#include "ts/ts.h"
29+
#include "ts/ink_assert.h"
30+
#include "ts/ink_defs.h"
31+
32+
#define PLUGIN_NAME "request_buffer"
33+
34+
#define TS_NULL_MUTEX NULL
35+
36+
static int
37+
request_buffer_plugin(TSCont contp, TSEvent event, void *edata)
38+
{
39+
TSDebug(PLUGIN_NAME, "request_buffer_plugin starting, event[%d]", event);
40+
TSHttpTxn txnp = (TSHttpTxn)(edata);
41+
if (event == TS_EVENT_HTTP_REQUEST_BUFFER_COMPLETE) {
42+
int len = 0;
43+
char *body = TSHttpTxnGetClientRequestBody(txnp, &len);
44+
TSDebug(PLUGIN_NAME, "request_buffer_plugin gets the request body with length[%d]", len);
45+
TSfree(body);
46+
TSContDestroy(contp);
47+
} else {
48+
ink_assert(0);
49+
}
50+
return 0;
51+
}
52+
53+
bool
54+
is_post_request(TSHttpTxn txnp)
55+
{
56+
TSMLoc req_loc;
57+
TSMBuffer req_bufp;
58+
if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_loc) == TS_ERROR) {
59+
TSError("Error while retrieving client request header\n");
60+
return false;
61+
}
62+
int method_len = 0;
63+
const char *method = TSHttpHdrMethodGet(req_bufp, req_loc, &method_len);
64+
if (method_len != (int)strlen(TS_HTTP_METHOD_POST) || strncasecmp(method, TS_HTTP_METHOD_POST, method_len) != 0) {
65+
TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc);
66+
return false;
67+
}
68+
TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc);
69+
return true;
70+
}
71+
72+
static int
73+
global_plugin(TSCont contp ATS_UNUSED, TSEvent event, void *edata)
74+
{
75+
TSDebug(PLUGIN_NAME, "transform_plugin starting");
76+
TSHttpTxn txnp = (TSHttpTxn)edata;
77+
78+
switch (event) {
79+
case TS_EVENT_HTTP_READ_REQUEST_HDR:
80+
if (is_post_request(txnp)) {
81+
TSHttpTxnConfigIntSet(txnp, TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED, 1);
82+
TSHttpTxnHookAdd(txnp, TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK, TSContCreate(request_buffer_plugin, TSMutexCreate()));
83+
}
84+
TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
85+
return 0;
86+
default:
87+
break;
88+
}
89+
90+
return 0;
91+
}
92+
93+
void
94+
TSPluginInit(int argc ATS_UNUSED, const char *argv[] ATS_UNUSED)
95+
{
96+
TSPluginRegistrationInfo info;
97+
98+
info.plugin_name = PLUGIN_NAME;
99+
info.vendor_name = "Apache Software Foundation";
100+
info.support_email = "dev@trafficserver.apache.org";
101+
102+
if (TSPluginRegister(&info) != TS_SUCCESS) {
103+
TSDebug(PLUGIN_NAME, "[%s] Plugin registration failed", PLUGIN_NAME);
104+
105+
goto Lerror;
106+
}
107+
108+
/* This is call we could use if we need to protect global data */
109+
/* TSReleaseAssert ((mutex = TSMutexCreate()) != TS_NULL_MUTEX); */
110+
111+
TSMutex mutex = TS_NULL_MUTEX;
112+
TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(global_plugin, mutex));
113+
TSDebug(PLUGIN_NAME, "[%s] Plugin registration succeeded", PLUGIN_NAME);
114+
return;
115+
116+
Lerror:
117+
TSDebug(PLUGIN_NAME, "[%s] Plugin disabled", PLUGIN_NAME);
118+
}

lib/ts/apidefs.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ typedef enum {
293293
TS_SSL_SERVER_VERIFY_HOOK,
294294
TS_SSL_SESSION_HOOK,
295295
TS_SSL_LAST_HOOK = TS_SSL_SESSION_HOOK,
296+
TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK,
296297
TS_HTTP_LAST_HOOK
297298
} TSHttpHookID;
298299

@@ -451,6 +452,7 @@ typedef enum {
451452
TS_EVENT_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED = 60022,
452453
TS_EVENT_VCONN_PRE_ACCEPT = 60023,
453454
TS_EVENT_LIFECYCLE_MSG = 60024,
455+
TS_EVENT_HTTP_REQUEST_BUFFER_COMPLETE = 60025,
454456
TS_EVENT_MGMT_UPDATE = 60100,
455457
TS_EVENT_INTERNAL_60200 = 60200,
456458
TS_EVENT_INTERNAL_60201 = 60201,
@@ -762,6 +764,7 @@ typedef enum {
762764
TS_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT,
763765
TS_CONFIG_HTTP_NORMALIZE_AE,
764766
TS_CONFIG_HTTP_INSERT_FORWARDED,
767+
TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED,
765768
TS_CONFIG_LAST_ENTRY
766769
} TSOverridableConfigKey;
767770

plugins/experimental/ts_lua/ts_lua_http_config.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ typedef enum {
132132
TS_LUA_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME = TS_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME,
133133
TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS = TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
134134
TS_LUA_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT = TS_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT,
135+
TS_LUA_CONFIG_HTTP_REQUEST_BUFFER_ENABLED = TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED,
135136
TS_LUA_CONFIG_LAST_ENTRY = TS_CONFIG_LAST_ENTRY,
136137
} TSLuaOverridableConfigKey;
137138

@@ -256,6 +257,7 @@ ts_lua_var_item ts_lua_http_config_vars[] = {
256257
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PARENT_PROXY_RETRY_TIME),
257258
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS),
258259
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT),
260+
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_REQUEST_BUFFER_ENABLED),
259261
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_LAST_ENTRY),
260262
};
261263

proxy/InkAPI.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8052,6 +8052,9 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overr
80528052
case TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED:
80538053
ret = _memberp_to_generic(&overridableHttpConfig->post_check_content_length_enabled, typep);
80548054
break;
8055+
case TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED:
8056+
ret = _memberp_to_generic(&overridableHttpConfig->request_buffer_enabled, typep);
8057+
break;
80558058
case TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER:
80568059
ret = _memberp_to_generic(&overridableHttpConfig->global_user_agent_header, typep);
80578060
break;
@@ -8527,6 +8530,8 @@ TSHttpTxnConfigFind(const char *name, int length, TSOverridableConfigKey *conf,
85278530
case 'd':
85288531
if (!strncmp(name, "proxy.config.http.forward_connect_method", length)) {
85298532
cnf = TS_CONFIG_HTTP_FORWARD_CONNECT_METHOD;
8533+
} else if (!strncmp(name, "proxy.config.http.request_buffer_enabled", length)) {
8534+
cnf = TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED;
85308535
}
85318536
break;
85328537
case 'e':
@@ -9520,3 +9525,31 @@ TSRegisterProtocolTag(const char *tag)
95209525
{
95219526
return nullptr;
95229527
}
9528+
9529+
tsapi char *
9530+
TSHttpTxnGetClientRequestBody(TSHttpTxn txnp, int *len)
9531+
{
9532+
char *ret = NULL;
9533+
9534+
sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
9535+
HttpSM *sm = (HttpSM *)txnp;
9536+
int64_t read_avail = sm->post_buffer_reader->read_avail();
9537+
if (read_avail == 0)
9538+
return NULL;
9539+
9540+
ret = (char *)TSmalloc(sizeof(char) * read_avail);
9541+
9542+
int64_t consumed = 0;
9543+
int64_t data_len = 0;
9544+
const char *char_data = NULL;
9545+
TSIOBufferBlock block = TSIOBufferReaderStart((TSIOBufferReader)sm->post_buffer_reader);
9546+
while (block != NULL) {
9547+
char_data = TSIOBufferBlockReadStart(block, (TSIOBufferReader)sm->post_buffer_reader, &data_len);
9548+
memcpy(ret + consumed, char_data, data_len);
9549+
consumed += data_len;
9550+
block = TSIOBufferBlockNext(block);
9551+
}
9552+
9553+
*len = (int)consumed;
9554+
return ret;
9555+
}

proxy/InkAPITest.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5546,6 +5546,7 @@ typedef enum {
55465546
ORIG_TS_SSL_SERVER_VERIFY_HOOK,
55475547
ORIG_TS_SSL_SESSION_HOOK,
55485548
ORIG_TS_SSL_LAST_HOOK = ORIG_TS_SSL_SESSION_HOOK,
5549+
ORIG_TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK,
55495550
ORIG_TS_HTTP_LAST_HOOK
55505551
} ORIG_TSHttpHookID;
55515552

@@ -7602,7 +7603,9 @@ const char *SDK_Overridable_Configs[TS_CONFIG_LAST_ENTRY] = {"proxy.config.url_r
76027603
"proxy.config.http.parent_proxy.per_parent_connect_attempts",
76037604
"proxy.config.http.parent_proxy.connect_attempts_timeout",
76047605
"proxy.config.http.normalize_ae",
7605-
"proxy.config.http.insert_forwarded"};
7606+
"proxy.config.http.insert_forwarded",
7607+
"proxy.config.http.request_buffer_enabled",
7608+
};
76067609

76077610
REGRESSION_TEST(SDK_API_OVERRIDABLE_CONFIGS)(RegressionTest *test, int /* atype ATS_UNUSED */, int *pstatus)
76087611
{

proxy/api/ts/ts.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,6 +2441,17 @@ tsapi const char *TSHttpTxnClientProtocolStackContains(TSHttpTxn txnp, char cons
24412441
tsapi const char *TSHttpSsnClientProtocolStackContains(TSHttpSsn ssnp, char const *tag);
24422442
tsapi const char *TSNormalizedProtocolTag(char const *tag);
24432443
tsapi const char *TSRegisterProtocolTag(char const *tag);
2444+
/**
2445+
Get the body (if applicable) from a client request. YOU MUST
2446+
free the pointer returned from this call. This call will return
2447+
NULL if no body exists. Changes to this string will not affect
2448+
the request body. The second parameter will contain the length
2449+
of the string returned.
2450+
2451+
@return char * that contains the entire buffered request body, this
2452+
must be freed by the caller using TSFree!
2453+
*/
2454+
tsapi char *TSHttpTxnGetClientRequestBody(TSHttpTxn txnp, int *length);
24442455

24452456
#ifdef __cplusplus
24462457
}

proxy/http/HttpConfig.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,7 @@ HttpConfig::startup()
957957
HttpEstablishStaticConfigLongLong(c.oride.flow_high_water_mark, "proxy.config.http.flow_control.high_water");
958958
HttpEstablishStaticConfigLongLong(c.oride.flow_low_water_mark, "proxy.config.http.flow_control.low_water");
959959
HttpEstablishStaticConfigByte(c.oride.post_check_content_length_enabled, "proxy.config.http.post.check.content_length.enabled");
960+
HttpEstablishStaticConfigByte(c.oride.request_buffer_enabled, "proxy.config.http.request_buffer_enabled");
960961
HttpEstablishStaticConfigByte(c.strict_uri_parsing, "proxy.config.http.strict_uri_parsing");
961962

962963
// [amc] This is a bit of a mess, need to figure out to make this cleaner.
@@ -1237,6 +1238,8 @@ HttpConfig::reconfigure()
12371238

12381239
params->oride.post_check_content_length_enabled = INT_TO_BOOL(m_master.oride.post_check_content_length_enabled);
12391240

1241+
params->oride.request_buffer_enabled = INT_TO_BOOL(m_master.oride.request_buffer_enabled);
1242+
12401243
params->oride.flow_control_enabled = INT_TO_BOOL(m_master.oride.flow_control_enabled);
12411244
params->oride.flow_high_water_mark = m_master.oride.flow_high_water_mark;
12421245
params->oride.flow_low_water_mark = m_master.oride.flow_low_water_mark;

proxy/http/HttpConfig.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,7 @@ struct OverridableHttpConfigParams {
446446
parent_failures_update_hostdb(0),
447447
cache_open_write_fail_action(0),
448448
post_check_content_length_enabled(1),
449+
request_buffer_enabled(0),
449450
ssl_client_verify_server(0),
450451
redirect_use_orig_cache_key(0),
451452
number_of_redirections(0),
@@ -619,6 +620,11 @@ struct OverridableHttpConfigParams {
619620
////////////////////////
620621
MgmtByte post_check_content_length_enabled;
621622

623+
////////////////////////////////////////////////
624+
// Buffer post body before connecting servers //
625+
////////////////////////////////////////////////
626+
MgmtByte request_buffer_enabled;
627+
622628
/////////////////////////////
623629
// server verification mode//
624630
/////////////////////////////

proxy/http/HttpDebugNames.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ HttpDebugNames::get_action_name(HttpTransact::StateMachineAction_t e)
360360
return ("SM_ACTION_DRAIN_REQUEST_BODY");
361361
#endif /* PROXY_DRAIN */
362362

363+
case HttpTransact::SM_ACTION_WAIT_FOR_FULL_BODY:
364+
return ("SM_ACTION_WAIT_FOR_FULL_BODY");
363365
case HttpTransact::SM_ACTION_API_SM_START:
364366
return ("SM_ACTION_API_SM_START");
365367
case HttpTransact::SM_ACTION_REDIRECT_READ:
@@ -446,6 +448,8 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t)
446448
return "TS_HTTP_SEND_RESPONSE_HDR_HOOK";
447449
case TS_HTTP_REQUEST_TRANSFORM_HOOK:
448450
return "TS_HTTP_REQUEST_TRANSFORM_HOOK";
451+
case TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK:
452+
return "TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK";
449453
case TS_HTTP_RESPONSE_TRANSFORM_HOOK:
450454
return "TS_HTTP_RESPONSE_TRANSFORM_HOOK";
451455
case TS_HTTP_SELECT_ALT_HOOK:

0 commit comments

Comments
 (0)