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.