You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2018/03/08 21:18:09 UTC
[trafficserver] 02/02: Adding slow_post_test
This is an automated email from the ASF dual-hosted git repository.
bcall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 88c74e4a09efc0d39f1d6d5292859bf9949bf973
Author: Zizhong Zhang <zi...@linkedin.com>
AuthorDate: Tue Jan 2 12:29:56 2018 -0800
Adding slow_post_test
---
tests/gold_tests/slow_post/gold/200.gold | 1 +
tests/gold_tests/slow_post/slow_post.test.py | 73 +++++++++++++
tests/gold_tests/slow_post/slow_post_client.py | 59 ++++++++++
tests/tools/plugins/request_buffer.c | 144 +++++++++++++++++++++++++
4 files changed, 277 insertions(+)
diff --git a/tests/gold_tests/slow_post/gold/200.gold b/tests/gold_tests/slow_post/gold/200.gold
new file mode 100644
index 0000000..08839f6
--- /dev/null
+++ b/tests/gold_tests/slow_post/gold/200.gold
@@ -0,0 +1 @@
+200
diff --git a/tests/gold_tests/slow_post/slow_post.test.py b/tests/gold_tests/slow_post/slow_post.test.py
new file mode 100644
index 0000000..906a87f
--- /dev/null
+++ b/tests/gold_tests/slow_post/slow_post.test.py
@@ -0,0 +1,73 @@
+'''
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+class SlowPostAttack:
+ def __init__(cls):
+ Test.Summary = 'Test how ATS handles the slow-post attack'
+ cls._origin_max_connections = 3
+ cls._slow_post_client = 'slow_post_client.py'
+ cls.setupOriginServer()
+ cls.setupTS()
+ cls._ts.Setup.CopyAs(cls._slow_post_client, Test.RunDirectory)
+
+ def setupOriginServer(self):
+ self._server = Test.MakeOriginServer("server")
+ request_header = {"headers": "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+ response_header = {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\nConnection: close\r\n\r\n",
+ "timestamp": "1469733493.993", "body": ""}
+ self._server.addResponse("sessionlog.json", request_header, response_header)
+ request_header2 = {"headers": "POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\nHost: www.example.com\r\nConnection: keep-alive\r\n\r\n",
+ "timestamp": "1469733493.993", "body": "a\r\na\r\na\r\n\r\n"}
+ response_header2 = {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\nConnection: close\r\n\r\n",
+ "timestamp": "1469733493.993", "body": ""}
+ self._server.addResponse("sessionlog.json", request_header2, response_header2)
+
+ def setupTS(self):
+ self._ts = Test.MakeATSProcess("ts", select_ports=False)
+ self._ts.Disk.remap_config.AddLine(
+ 'map / http://127.0.0.1:{0}'.format(self._server.Variables.Port)
+ )
+ # This plugin can enable request buffer for POST.
+ self._ts.Disk.plugin_config.AddLine(
+ 'request_buffer.so'
+ )
+ Test.PreparePlugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins', 'request_buffer.c'), self._ts)
+ self._ts.Disk.records_config.update({
+ 'proxy.config.diags.debug.enabled': 1,
+ 'proxy.config.diags.debug.tags': 'http',
+ 'proxy.config.http.origin_max_connections': self._origin_max_connections,
+ # Disable queueing when connection reaches limit
+ 'proxy.config.http.origin_max_connections_queue': 0,
+ })
+
+ def run(self):
+ tr = Test.AddTestRun()
+ tr.Processes.Default.Command = 'python3 {0} -p {1} -c {2}'.format(
+ self._slow_post_client, self._ts.Variables.port, self._origin_max_connections)
+ tr.Processes.Default.ReturnCode = 0
+ tr.Processes.Default.StartBefore(self._server)
+ tr.Processes.Default.StartBefore(Test.Processes.ts)
+ tr.Processes.Default.Streams.stdout = "gold/200.gold"
+
+
+Test.Summary = 'Test how ATS handles the slow-post attack'
+slowPostAttack = SlowPostAttack()
+slowPostAttack.run()
diff --git a/tests/gold_tests/slow_post/slow_post_client.py b/tests/gold_tests/slow_post/slow_post_client.py
new file mode 100644
index 0000000..a132a67
--- /dev/null
+++ b/tests/gold_tests/slow_post/slow_post_client.py
@@ -0,0 +1,59 @@
+'''
+'''
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import time
+import threading
+import requests
+import argparse
+
+
+def gen(slow_time):
+ for _ in range(slow_time):
+ yield b'a'
+ time.sleep(1)
+
+
+def slow_post(port, slow_time):
+ requests.post('http://127.0.0.1:{0}/'.format(port, ), data=gen(slow_time))
+
+
+def makerequest(port, connection_limit):
+ client_timeout = 3
+ for i in range(connection_limit):
+ t = threading.Thread(target=slow_post, args=(port, client_timeout + 10))
+ t.daemon = True
+ t.start()
+ time.sleep(1)
+ r = requests.get('http://127.0.0.1:{0}/'.format(port,))
+ print(r.status_code)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--port", "-p",
+ type=int,
+ help="Port to use")
+ parser.add_argument("--connectionlimit", "-c",
+ type=int,
+ help="connection limit")
+ args = parser.parse_args()
+ makerequest(args.port, args.connectionlimit)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tests/tools/plugins/request_buffer.c b/tests/tools/plugins/request_buffer.c
new file mode 100644
index 0000000..7e6df14
--- /dev/null
+++ b/tests/tools/plugins/request_buffer.c
@@ -0,0 +1,144 @@
+/** @file
+
+ A brief file description
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ts/ts.h"
+
+#define PLUGIN_NAME "request_buffer"
+
+#define TS_NULL_MUTEX NULL
+
+static char *
+request_body_get(TSHttpTxn txnp, int *len)
+{
+ char *ret = NULL;
+ TSIOBufferReader post_buffer_reader = TSHttpTxnPostBufferReaderGet(txnp);
+ int64_t read_avail = TSIOBufferReaderAvail(post_buffer_reader);
+ if (read_avail == 0) {
+ TSIOBufferReaderFree(post_buffer_reader);
+ return NULL;
+ }
+
+ ret = (char *)TSmalloc(sizeof(char) * read_avail);
+
+ int64_t consumed = 0;
+ int64_t data_len = 0;
+ const char *char_data = NULL;
+ TSIOBufferBlock block = TSIOBufferReaderStart(post_buffer_reader);
+ while (block != NULL) {
+ char_data = TSIOBufferBlockReadStart(block, post_buffer_reader, &data_len);
+ memcpy(ret + consumed, char_data, data_len);
+ consumed += data_len;
+ block = TSIOBufferBlockNext(block);
+ }
+ TSIOBufferReaderFree(post_buffer_reader);
+
+ *len = (int)consumed;
+ return ret;
+}
+
+static int
+request_buffer_plugin(TSCont contp, TSEvent event, void *edata)
+{
+ TSDebug(PLUGIN_NAME, "request_buffer_plugin starting, event[%d]", event);
+ TSHttpTxn txnp = (TSHttpTxn)(edata);
+ if (event == TS_EVENT_HTTP_REQUEST_BUFFER_COMPLETE) {
+ int len = 0;
+ char *body = request_body_get(txnp, &len);
+ TSDebug(PLUGIN_NAME, "request_buffer_plugin gets the request body with length[%d]", len);
+ TSfree(body);
+ TSContDestroy(contp);
+ }
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 0;
+}
+
+bool
+is_post_request(TSHttpTxn txnp)
+{
+ TSMLoc req_loc;
+ TSMBuffer req_bufp;
+ if (TSHttpTxnClientReqGet(txnp, &req_bufp, &req_loc) == TS_ERROR) {
+ TSError("Error while retrieving client request header\n");
+ return false;
+ }
+ int method_len = 0;
+ const char *method = TSHttpHdrMethodGet(req_bufp, req_loc, &method_len);
+ if (method_len != (int)strlen(TS_HTTP_METHOD_POST) || strncasecmp(method, TS_HTTP_METHOD_POST, method_len) != 0) {
+ TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc);
+ return false;
+ }
+ TSHandleMLocRelease(req_bufp, TS_NULL_MLOC, req_loc);
+ return true;
+}
+
+static int
+global_plugin(TSCont contp, TSEvent event, void *edata)
+{
+ TSDebug(PLUGIN_NAME, "transform_plugin starting");
+ TSHttpTxn txnp = (TSHttpTxn)edata;
+
+ switch (event) {
+ case TS_EVENT_HTTP_READ_REQUEST_HDR:
+ if (is_post_request(txnp)) {
+ TSHttpTxnConfigIntSet(txnp, TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED, 1);
+ TSHttpTxnHookAdd(txnp, TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK, TSContCreate(request_buffer_plugin, TSMutexCreate()));
+ }
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 0;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+
+ info.plugin_name = PLUGIN_NAME;
+ info.vendor_name = "Apache Software Foundation";
+ info.support_email = "dev@trafficserver.apache.org";
+
+ if (TSPluginRegister(&info) != TS_SUCCESS) {
+ TSDebug(PLUGIN_NAME, "[%s] Plugin registration failed", PLUGIN_NAME);
+
+ goto Lerror;
+ }
+
+ /* This is call we could use if we need to protect global data */
+ /* TSReleaseAssert ((mutex = TSMutexCreate()) != TS_NULL_MUTEX); */
+
+ TSMutex mutex = TS_NULL_MUTEX;
+ TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(global_plugin, mutex));
+ TSDebug(PLUGIN_NAME, "[%s] Plugin registration succeeded", PLUGIN_NAME);
+ return;
+
+Lerror:
+ TSDebug(PLUGIN_NAME, "[%s] Plugin disabled", PLUGIN_NAME);
+}
--
To stop receiving notification emails like this one, please contact
bcall@apache.org.