trafficserver-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bc...@apache.org
Subject [trafficserver] 01/02: TS-4042: Add feature to buffer request body before making downstream requests
Date Thu, 08 Mar 2018 21:18:08 GMT
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 78cb6c9bf86e8d72c79a9084604bc25520ef57d7
Author: Zizhong Zhang <zizhang@linkedin.com>
AuthorDate: Fri Aug 4 16:27:48 2017 -0700

    TS-4042: Add feature to buffer request body before making downstream requests
---
 example/Makefile.am                              |   2 +
 example/request_buffer/request_buffer.c          | 148 +++++++++++++++++++++++
 lib/ts/apidefs.h.in                              |   3 +
 plugins/experimental/ts_lua/ts_lua_http_config.c |   2 +
 proxy/InkAPI.cc                                  |  13 ++
 proxy/InkAPITest.cc                              |   6 +-
 proxy/api/ts/ts.h                                |   5 +
 proxy/http/HttpConfig.cc                         |   3 +
 proxy/http/HttpConfig.h                          |   6 +
 proxy/http/HttpDebugNames.cc                     |   6 +
 proxy/http/HttpSM.cc                             |  95 ++++++++++++++-
 proxy/http/HttpSM.h                              |  48 ++++++++
 proxy/http/HttpTransact.cc                       | 130 +++++++++++---------
 proxy/http/HttpTransact.h                        |   3 +
 proxy/http/HttpTunnel.cc                         |  33 +++--
 proxy/http/HttpTunnel.h                          |   9 +-
 16 files changed, 424 insertions(+), 88 deletions(-)

diff --git a/example/Makefile.am b/example/Makefile.am
index c79c3a3..6c816ee 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -32,6 +32,7 @@ example_Plugins = \
 	blacklist_0.la \
 	blacklist_1.la \
 	bnull_transform.la \
+	request_buffer.la \
 	cache_scan.la \
 	file_1.la \
 	hello.la \
@@ -99,6 +100,7 @@ basic_auth_la_SOURCES = basic_auth/basic_auth.c
 blacklist_0_la_SOURCES = blacklist_0/blacklist_0.c
 blacklist_1_la_SOURCES = blacklist_1/blacklist_1.c
 bnull_transform_la_SOURCES = bnull_transform/bnull_transform.c
+request_buffer_la_SOURCES = request_buffer/request_buffer.c
 cache_scan_la_SOURCES = cache_scan/cache_scan.cc
 file_1_la_SOURCES = file_1/file_1.c
 hello_la_SOURCES = hello/hello.c
diff --git a/example/request_buffer/request_buffer.c b/example/request_buffer/request_buffer.c
new file mode 100644
index 0000000..a75650f
--- /dev/null
+++ b/example/request_buffer/request_buffer.c
@@ -0,0 +1,148 @@
+/** @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"
+#include "ts/ink_assert.h"
+#include "ts/ink_defs.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);
+  } else {
+    ink_assert(0);
+  }
+  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 ATS_UNUSED, 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 ATS_UNUSED, const char *argv[] ATS_UNUSED)
+{
+  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);
+}
diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
index 0889181..a035812 100644
--- a/lib/ts/apidefs.h.in
+++ b/lib/ts/apidefs.h.in
@@ -294,6 +294,7 @@ typedef enum {
   TS_SSL_VERIFY_CLIENT_HOOK,
   TS_SSL_SESSION_HOOK,
   TS_SSL_LAST_HOOK = TS_SSL_SESSION_HOOK,
+  TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK = 23,
   TS_HTTP_LAST_HOOK
 } TSHttpHookID;
 
@@ -452,6 +453,7 @@ typedef enum {
   TS_EVENT_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED = 60022,
   TS_EVENT_VCONN_PRE_ACCEPT                     = 60023,
   TS_EVENT_LIFECYCLE_MSG                        = 60024,
+  TS_EVENT_HTTP_REQUEST_BUFFER_COMPLETE         = 60025,
   TS_EVENT_MGMT_UPDATE                          = 60100,
   TS_EVENT_INTERNAL_60200                       = 60200,
   TS_EVENT_INTERNAL_60201                       = 60201,
@@ -766,6 +768,7 @@ typedef enum {
   TS_CONFIG_HTTP_NORMALIZE_AE,
   TS_CONFIG_HTTP_INSERT_FORWARDED,
   TS_CONFIG_HTTP_ALLOW_MULTI_RANGE,
+  TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED,
   TS_CONFIG_LAST_ENTRY
 } TSOverridableConfigKey;
 
diff --git a/plugins/experimental/ts_lua/ts_lua_http_config.c b/plugins/experimental/ts_lua/ts_lua_http_config.c
index 8bf78c7..c4b7593 100644
--- a/plugins/experimental/ts_lua/ts_lua_http_config.c
+++ b/plugins/experimental/ts_lua/ts_lua_http_config.c
@@ -133,6 +133,7 @@ typedef enum {
   TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS              = TS_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS,
   TS_LUA_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT           = TS_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT,
   TS_LUA_CONFIG_HTTP_ALLOW_MULTI_RANGE                        = TS_CONFIG_HTTP_ALLOW_MULTI_RANGE,
+  TS_LUA_CONFIG_HTTP_REQUEST_BUFFER_ENABLED                   = TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED,
   TS_LUA_CONFIG_LAST_ENTRY                                    = TS_CONFIG_LAST_ENTRY,
 } TSLuaOverridableConfigKey;
 
@@ -258,6 +259,7 @@ ts_lua_var_item ts_lua_http_config_vars[] = {
   TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PER_PARENT_CONNECT_ATTEMPTS),
   TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_PARENT_CONNECT_ATTEMPT_TIMEOUT),
   TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_ALLOW_MULTI_RANGE),
+  TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_REQUEST_BUFFER_ENABLED),
   TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_LAST_ENTRY),
 };
 
diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc
index 5e35f89..5dd1319 100644
--- a/proxy/InkAPI.cc
+++ b/proxy/InkAPI.cc
@@ -8115,6 +8115,9 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams
*overr
   case TS_CONFIG_HTTP_POST_CHECK_CONTENT_LENGTH_ENABLED:
     ret = _memberp_to_generic(&overridableHttpConfig->post_check_content_length_enabled,
typep);
     break;
+  case TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED:
+    ret = _memberp_to_generic(&overridableHttpConfig->request_buffer_enabled, typep);
+    break;
   case TS_CONFIG_HTTP_GLOBAL_USER_AGENT_HEADER:
     ret = _memberp_to_generic(&overridableHttpConfig->global_user_agent_header, typep);
     break;
@@ -8595,6 +8598,8 @@ TSHttpTxnConfigFind(const char *name, int length, TSOverridableConfigKey
*conf,
     case 'd':
       if (!strncmp(name, "proxy.config.http.forward_connect_method", length)) {
         cnf = TS_CONFIG_HTTP_FORWARD_CONNECT_METHOD;
+      } else if (!strncmp(name, "proxy.config.http.request_buffer_enabled", length)) {
+        cnf = TS_CONFIG_HTTP_REQUEST_BUFFER_ENABLED;
       }
       break;
     case 'e':
@@ -9630,3 +9635,11 @@ TSRemapToUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp)
 {
   return remapUrlGet(txnp, urlLocp, &UrlMappingContainer::getToURL);
 }
+
+tsapi TSIOBufferReader
+TSHttpTxnPostBufferReaderGet(TSHttpTxn txnp)
+{
+  sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
+  HttpSM *sm = (HttpSM *)txnp;
+  return (TSIOBufferReader)sm->get_postbuf_clone_reader();
+}
diff --git a/proxy/InkAPITest.cc b/proxy/InkAPITest.cc
index 12b6aae..8bcdae8 100644
--- a/proxy/InkAPITest.cc
+++ b/proxy/InkAPITest.cc
@@ -5546,7 +5546,8 @@ typedef enum {
   ORIG_TS_SSL_SERVER_VERIFY_HOOK,
   ORIG_TS_SSL_VERIFY_CLIENT_HOOK,
   ORIG_TS_SSL_SESSION_HOOK,
-  ORIG_TS_SSL_LAST_HOOK = ORIG_TS_SSL_SESSION_HOOK,
+  ORIG_TS_SSL_LAST_HOOK                          = ORIG_TS_SSL_SESSION_HOOK,
+  ORIG_TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK = 23,
   ORIG_TS_HTTP_LAST_HOOK
 } ORIG_TSHttpHookID;
 
@@ -7604,7 +7605,8 @@ const char *SDK_Overridable_Configs[TS_CONFIG_LAST_ENTRY] = {"proxy.config.url_r
                                                              "proxy.config.http.parent_proxy.connect_attempts_timeout",
                                                              "proxy.config.http.normalize_ae",
                                                              "proxy.config.http.insert_forwarded",
-                                                             "proxy.config.http.allow_multi_range"};
+                                                             "proxy.config.http.allow_multi_range",
+                                                             "proxy.config.http.request_buffer_enabled"};
 
 REGRESSION_TEST(SDK_API_OVERRIDABLE_CONFIGS)(RegressionTest *test, int /* atype ATS_UNUSED
*/, int *pstatus)
 {
diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h
index 7b1b752..2f07c64 100644
--- a/proxy/api/ts/ts.h
+++ b/proxy/api/ts/ts.h
@@ -2460,6 +2460,11 @@ tsapi TSReturnCode TSRemapFromUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp);
 //
 tsapi TSReturnCode TSRemapToUrlGet(TSHttpTxn txnp, TSMLoc *urlLocp);
 
+/*
+ * Get a TSIOBufferReader to read the buffered body. The return value needs to be freed.
+ */
+tsapi TSIOBufferReader TSHttpTxnPostBufferReaderGet(TSHttpTxn txnp);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
index 6f08ead..65990ef 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -966,6 +966,7 @@ HttpConfig::startup()
   HttpEstablishStaticConfigLongLong(c.oride.flow_high_water_mark, "proxy.config.http.flow_control.high_water");
   HttpEstablishStaticConfigLongLong(c.oride.flow_low_water_mark, "proxy.config.http.flow_control.low_water");
   HttpEstablishStaticConfigByte(c.oride.post_check_content_length_enabled, "proxy.config.http.post.check.content_length.enabled");
+  HttpEstablishStaticConfigByte(c.oride.request_buffer_enabled, "proxy.config.http.request_buffer_enabled");
   HttpEstablishStaticConfigByte(c.strict_uri_parsing, "proxy.config.http.strict_uri_parsing");
 
   // [amc] This is a bit of a mess, need to figure out to make this cleaner.
@@ -1247,6 +1248,8 @@ HttpConfig::reconfigure()
 
   params->oride.post_check_content_length_enabled = INT_TO_BOOL(m_master.oride.post_check_content_length_enabled);
 
+  params->oride.request_buffer_enabled = INT_TO_BOOL(m_master.oride.request_buffer_enabled);
+
   params->oride.flow_control_enabled = INT_TO_BOOL(m_master.oride.flow_control_enabled);
   params->oride.flow_high_water_mark = m_master.oride.flow_high_water_mark;
   params->oride.flow_low_water_mark  = m_master.oride.flow_low_water_mark;
diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
index a01a2d9..8a22a43 100644
--- a/proxy/http/HttpConfig.h
+++ b/proxy/http/HttpConfig.h
@@ -450,6 +450,7 @@ struct OverridableHttpConfigParams {
       parent_failures_update_hostdb(0),
       cache_open_write_fail_action(0),
       post_check_content_length_enabled(1),
+      request_buffer_enabled(0),
       ssl_client_verify_server(0),
       redirect_use_orig_cache_key(0),
       number_of_redirections(0),
@@ -624,6 +625,11 @@ struct OverridableHttpConfigParams {
   ////////////////////////
   MgmtByte post_check_content_length_enabled;
 
+  ////////////////////////////////////////////////
+  // Buffer post body before connecting servers //
+  ////////////////////////////////////////////////
+  MgmtByte request_buffer_enabled;
+
   /////////////////////////////
   // server verification mode//
   /////////////////////////////
diff --git a/proxy/http/HttpDebugNames.cc b/proxy/http/HttpDebugNames.cc
index e135eef..048febd 100644
--- a/proxy/http/HttpDebugNames.cc
+++ b/proxy/http/HttpDebugNames.cc
@@ -352,6 +352,10 @@ HttpDebugNames::get_action_name(HttpTransact::StateMachineAction_t e)
     return ("SM_ACTION_DRAIN_REQUEST_BODY");
 #endif /* PROXY_DRAIN */
 
+  case HttpTransact::SM_ACTION_WAIT_FOR_FULL_BODY:
+    return ("SM_ACTION_WAIT_FOR_FULL_BODY");
+  case HttpTransact::SM_ACTION_REQUEST_BUFFER_READ_COMPLETE:
+    return ("SM_ACTION_REQUEST_BUFFER_READ_COMPLETE");
   case HttpTransact::SM_ACTION_API_SM_START:
     return ("SM_ACTION_API_SM_START");
   case HttpTransact::SM_ACTION_REDIRECT_READ:
@@ -438,6 +442,8 @@ HttpDebugNames::get_api_hook_name(TSHttpHookID t)
     return "TS_HTTP_SEND_RESPONSE_HDR_HOOK";
   case TS_HTTP_REQUEST_TRANSFORM_HOOK:
     return "TS_HTTP_REQUEST_TRANSFORM_HOOK";
+  case TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK:
+    return "TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK";
   case TS_HTTP_RESPONSE_TRANSFORM_HOOK:
     return "TS_HTTP_RESPONSE_TRANSFORM_HOOK";
   case TS_HTTP_SELECT_ALT_HOOK:
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index fded2c2..c279f57 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -838,6 +838,48 @@ HttpSM::state_drain_client_request_body(int event, void *data)
 }
 #endif /* PROXY_DRAIN */
 
+void
+HttpSM::wait_for_full_body()
+{
+  is_waiting_for_full_body = true;
+  HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::tunnel_handler_post);
+  bool chunked = (t_state.client_info.transfer_encoding == HttpTransact::CHUNKED_ENCODING);
+  int64_t alloc_index;
+  HttpTunnelProducer *p = nullptr;
+
+  // content length is undefined, use default buffer size
+  if (t_state.hdr_info.request_content_length == HTTP_UNDEFINED_CL) {
+    alloc_index = (int)t_state.txn_conf->default_buffer_size_index;
+    if (alloc_index < MIN_CONFIG_BUFFER_SIZE_INDEX || alloc_index > MAX_BUFFER_SIZE_INDEX)
{
+      alloc_index = DEFAULT_REQUEST_BUFFER_SIZE_INDEX;
+    }
+  } else {
+    alloc_index = buffer_size_to_index(t_state.hdr_info.request_content_length);
+  }
+  MIOBuffer *post_buffer    = new_MIOBuffer(alloc_index);
+  IOBufferReader *buf_start = post_buffer->alloc_reader();
+
+  this->_postbuf.init(post_buffer->clone_reader(buf_start));
+
+  // Note: Many browsers, Netscape and IE included send two extra
+  //  bytes (CRLF) at the end of the post.  We just ignore those
+  //  bytes since the sending them is not spec
+
+  // Next order of business if copy the remaining data from the
+  //  header buffer into new buffer
+  int64_t post_bytes        = chunked ? INT64_MAX : t_state.hdr_info.request_content_length;
+  client_request_body_bytes = post_buffer->write(ua_buffer_reader, chunked ? ua_buffer_reader->read_avail()
: post_bytes);
+
+  ua_buffer_reader->consume(client_request_body_bytes);
+  p = tunnel.add_producer(ua_entry->vc, post_bytes, buf_start, &HttpSM::tunnel_handler_post_ua,
HT_BUFFER_READ, "ua post buffer");
+  if (chunked) {
+    tunnel.set_producer_chunking_action(p, 0, TCA_PASSTHRU_CHUNKED_CONTENT);
+  }
+  ua_entry->in_tunnel = true;
+  ua_txn->set_inactivity_timeout(HRTIME_SECONDS(t_state.txn_conf->transaction_no_activity_timeout_in));
+  tunnel.tunnel_run(p);
+}
+
 int
 HttpSM::state_watch_for_client_abort(int event, void *data)
 {
@@ -1601,6 +1643,7 @@ HttpSM::handle_api_return()
   case HttpTransact::SM_ACTION_API_PRE_REMAP:
   case HttpTransact::SM_ACTION_API_POST_REMAP:
   case HttpTransact::SM_ACTION_API_READ_REQUEST_HDR:
+  case HttpTransact::SM_ACTION_REQUEST_BUFFER_READ_COMPLETE:
   case HttpTransact::SM_ACTION_API_OS_DNS:
   case HttpTransact::SM_ACTION_API_READ_RESPONSE_HDR:
     call_transact_and_set_next_state(nullptr);
@@ -2616,7 +2659,7 @@ HttpSM::main_handler(int event, void *data)
 void
 HttpSM::tunnel_handler_post_or_put(HttpTunnelProducer *p)
 {
-  ink_assert(p->vc_type == HT_HTTP_CLIENT);
+  ink_assert(p->vc_type == HT_HTTP_CLIENT || (p->handler_state == HTTP_SM_POST_UA_FAIL
&& p->vc_type == HT_BUFFER_READ));
   HttpTunnelConsumer *c;
 
   // If there is a post transform, remove it's entry from the State
@@ -2715,7 +2758,12 @@ HttpSM::tunnel_handler_post(int event, void *data)
   // The tunnel calls this when it is done
 
   int p_handler_state = p->handler_state;
-  tunnel_handler_post_or_put(p);
+  if (is_waiting_for_full_body && !this->is_postbuf_valid()) {
+    p_handler_state = HTTP_SM_POST_SERVER_FAIL;
+  }
+  if (p->vc_type != HT_BUFFER_READ) {
+    tunnel_handler_post_or_put(p);
+  }
 
   switch (p_handler_state) {
   case HTTP_SM_POST_SERVER_FAIL:
@@ -2725,6 +2773,14 @@ HttpSM::tunnel_handler_post(int event, void *data)
     break;
   case HTTP_SM_POST_SUCCESS:
     // It's time to start reading the response
+    if (is_waiting_for_full_body) {
+      is_waiting_for_full_body  = false;
+      is_using_post_buffer      = true;
+      client_request_body_bytes = this->postbuf_buffer_avail();
+
+      call_transact_and_set_next_state(HttpTransact::HandleRequestBufferDone);
+      break;
+    }
     setup_server_read_response_header();
     break;
   default:
@@ -3462,7 +3518,7 @@ HttpSM::tunnel_handler_post_ua(int event, HttpTunnelProducer *p)
     //   we were setting it again to true but incorrectly in
     //   the case of a transform
     hsm_release_assert(ua_entry->in_tunnel == true);
-    if (p->consumer_list.head->vc_type == HT_TRANSFORM) {
+    if (p->consumer_list.head && p->consumer_list.head->vc_type == HT_TRANSFORM)
{
       hsm_release_assert(post_transform_info.entry->in_tunnel == true);
     } else if (server_entry != nullptr) {
       hsm_release_assert(server_entry->in_tunnel == true);
@@ -3482,6 +3538,7 @@ HttpSM::tunnel_handler_post_ua(int event, HttpTunnelProducer *p)
         tunnel.local_finish_all(p);
       }
     }
+
     // Initiate another read to watch catch aborts and
     //   timeouts
     ua_entry->vc_handler = &HttpSM::state_watch_for_client_abort;
@@ -3507,6 +3564,7 @@ HttpSM::tunnel_handler_for_partial_post(int event, void * /* data ATS_UNUSED
*/)
   tunnel.reset();
 
   t_state.redirect_info.redirect_in_process = false;
+  is_using_post_buffer                      = false;
 
   if (post_failed) {
     post_failed = false;
@@ -5062,6 +5120,9 @@ HttpSM::do_api_callout_internal()
   case HttpTransact::SM_ACTION_API_READ_REQUEST_HDR:
     cur_hook_id = TS_HTTP_READ_REQUEST_HDR_HOOK;
     break;
+  case HttpTransact::SM_ACTION_REQUEST_BUFFER_READ_COMPLETE:
+    cur_hook_id = TS_HTTP_REQUEST_BUFFER_READ_COMPLETE_HOOK;
+    break;
   case HttpTransact::SM_ACTION_API_OS_DNS:
     cur_hook_id = TS_HTTP_OS_DNS_HOOK;
     break;
@@ -5302,8 +5363,12 @@ HttpSM::handle_post_failure()
   STATE_ENTER(&HttpSM::handle_post_failure, VC_EVENT_NONE);
 
   ink_assert(ua_entry->vc == ua_txn);
-  ink_assert(server_entry->eos == true);
+  ink_assert(is_waiting_for_full_body || server_entry->eos == true);
 
+  if (is_waiting_for_full_body) {
+    call_transact_and_set_next_state(HttpTransact::Forbidden);
+    return;
+  }
   // First order of business is to clean up from
   //  the tunnel
   // note: since the tunnel is providing the buffer for a lingering
@@ -5586,7 +5651,8 @@ HttpSM::do_setup_post_tunnel(HttpVC_t to_vc_type)
   // YTS Team, yamsat Plugin
   // if redirect_in_process and redirection is enabled add static producer
 
-  if (t_state.redirect_info.redirect_in_process && enable_redirection &&
(this->_postbuf.postdata_copy_buffer_start != nullptr)) {
+  if (is_using_post_buffer ||
+      (t_state.redirect_info.redirect_in_process && enable_redirection &&
this->_postbuf.postdata_copy_buffer_start != nullptr)) {
     post_redirect = true;
     // copy the post data into a new producer buffer for static producer
     MIOBuffer *postdata_producer_buffer      = new_empty_MIOBuffer();
@@ -7176,6 +7242,7 @@ HttpSM::set_next_state()
   case HttpTransact::SM_ACTION_API_PRE_REMAP:
   case HttpTransact::SM_ACTION_API_POST_REMAP:
   case HttpTransact::SM_ACTION_API_READ_REQUEST_HDR:
+  case HttpTransact::SM_ACTION_REQUEST_BUFFER_READ_COMPLETE:
   case HttpTransact::SM_ACTION_API_OS_DNS:
   case HttpTransact::SM_ACTION_API_SEND_REQUEST_HDR:
   case HttpTransact::SM_ACTION_API_READ_CACHE_HDR:
@@ -7586,6 +7653,11 @@ HttpSM::set_next_state()
   }
 #endif /* PROXY_DRAIN */
 
+  case HttpTransact::SM_ACTION_WAIT_FOR_FULL_BODY: {
+    wait_for_full_body();
+    break;
+  }
+
   case HttpTransact::SM_ACTION_CONTINUE: {
     ink_release_assert(!"Not implemented");
     break;
@@ -8031,12 +8103,21 @@ HttpSM::find_proto_string(HTTPVersion version) const
 void
 PostDataBuffers::copy_partial_post_data()
 {
-  this->postdata_copy_buffer->write(this->ua_buffer_reader);
+  if (post_data_buffer_done) {
+    return;
+  }
   Debug("http_redirect", "[PostDataBuffers::copy_partial_post_data] wrote %" PRId64 " bytes
to buffers %" PRId64 "",
         this->ua_buffer_reader->read_avail(), this->postdata_copy_buffer_start->read_avail());
+  this->postdata_copy_buffer->write(this->ua_buffer_reader);
   this->ua_buffer_reader->consume(this->ua_buffer_reader->read_avail());
 }
 
+IOBufferReader *
+PostDataBuffers::get_post_data_buffer_clone_reader()
+{
+  return this->postdata_copy_buffer->clone_reader(this->postdata_copy_buffer_start);
+}
+
 // YTS Team, yamsat Plugin
 // Allocating the post data buffers
 void
@@ -8047,6 +8128,7 @@ PostDataBuffers::init(IOBufferReader *ua_reader)
   this->ua_buffer_reader = ua_reader;
 
   if (this->postdata_copy_buffer == nullptr) {
+    this->post_data_buffer_done = false;
     ink_assert(this->postdata_copy_buffer_start == nullptr);
     this->postdata_copy_buffer       = new_empty_MIOBuffer(BUFFER_SIZE_INDEX_4K);
     this->postdata_copy_buffer_start = this->postdata_copy_buffer->alloc_reader();
@@ -8067,6 +8149,7 @@ PostDataBuffers::clear()
     this->postdata_copy_buffer       = nullptr;
     this->postdata_copy_buffer_start = nullptr; // deallocated by the buffer
   }
+  this->post_data_buffer_done = false;
 }
 
 PostDataBuffers::~PostDataBuffers()
diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h
index d98d3a4..4cb535b 100644
--- a/proxy/http/HttpSM.h
+++ b/proxy/http/HttpSM.h
@@ -184,10 +184,27 @@ public:
   MIOBuffer *postdata_copy_buffer            = nullptr;
   IOBufferReader *postdata_copy_buffer_start = nullptr;
   IOBufferReader *ua_buffer_reader           = nullptr;
+  bool post_data_buffer_done                 = false;
 
   void clear();
   void init(IOBufferReader *ua_reader);
   void copy_partial_post_data();
+  IOBufferReader *get_post_data_buffer_clone_reader();
+  void
+  set_post_data_buffer_done(bool done)
+  {
+    post_data_buffer_done = done;
+  }
+  bool
+  get_post_data_buffer_done()
+  {
+    return post_data_buffer_done;
+  }
+  bool
+  is_valid()
+  {
+    return postdata_copy_buffer_start != nullptr;
+  }
 
   ~PostDataBuffers();
 };
@@ -318,6 +335,10 @@ public:
   void disable_redirect();
   void postbuf_copy_partial_data();
   void postbuf_init(IOBufferReader *ua_reader);
+  void set_postbuf_done(bool done);
+  bool get_postbuf_done();
+  bool is_postbuf_valid();
+  IOBufferReader *get_postbuf_clone_reader();
 
 protected:
   int reentrancy_count = 0;
@@ -454,6 +475,8 @@ protected:
   void do_drain_request_body();
 #endif
 
+  void wait_for_full_body();
+
   virtual void handle_api_return();
   void handle_server_setup_error(int event, void *data);
   void handle_http_server_open();
@@ -528,6 +551,8 @@ public:
   const char *client_cipher_suite = "-";
   int server_transact_count       = 0;
   bool server_connection_is_ssl   = false;
+  bool is_waiting_for_full_body   = false;
+  bool is_using_post_buffer       = false;
 
   TransactionMilestones milestones;
   ink_hrtime api_timer = 0;
@@ -731,4 +756,27 @@ HttpSM::postbuf_init(IOBufferReader *ua_reader)
   this->_postbuf.init(ua_reader);
 }
 
+inline void
+HttpSM::set_postbuf_done(bool done)
+{
+  this->_postbuf.set_post_data_buffer_done(done);
+}
+
+inline bool
+HttpSM::get_postbuf_done()
+{
+  return this->_postbuf.get_post_data_buffer_done();
+}
+
+inline bool
+HttpSM::is_postbuf_valid()
+{
+  return this->_postbuf.is_valid();
+}
+
+inline IOBufferReader *
+HttpSM::get_postbuf_clone_reader()
+{
+  return this->_postbuf.get_post_data_buffer_clone_reader();
+}
 #endif
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 5f16743..9af7900 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -1123,77 +1123,81 @@ HttpTransact::HandleRequest(State *s)
 {
   TxnDebug("http_trans", "START HttpTransact::HandleRequest");
 
-  ink_assert(!s->hdr_info.server_request.valid());
+  if (!s->request_data.hdr) {
+    ink_assert(!s->hdr_info.server_request.valid());
 
-  HTTP_INCREMENT_DYN_STAT(http_incoming_requests_stat);
+    HTTP_INCREMENT_DYN_STAT(http_incoming_requests_stat);
 
-  if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
-    HTTP_INCREMENT_DYN_STAT(https_incoming_requests_stat);
-  }
-
-  ///////////////////////////////////////////////
-  // if request is bad, return error response  //
-  ///////////////////////////////////////////////
-
-  if (!(is_request_valid(s, &s->hdr_info.client_request))) {
-    HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
-    TxnDebug("http_seq", "[HttpTransact::HandleRequest] request invalid.");
-    s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
-    //  s->next_action = HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
-    return;
-  }
-  TxnDebug("http_seq", "[HttpTransact::HandleRequest] request valid.");
+    if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
+      HTTP_INCREMENT_DYN_STAT(https_incoming_requests_stat);
+    }
 
-  if (is_debug_tag_set("http_chdr_describe")) {
-    obj_describe(s->hdr_info.client_request.m_http, true);
-  }
+    ///////////////////////////////////////////////
+    // if request is bad, return error response  //
+    ///////////////////////////////////////////////
 
-  // at this point we are guaranteed that the request is good and acceptable.
-  // initialize some state variables from the request (client version,
-  // client keep-alive, cache action, etc.
-  initialize_state_variables_from_request(s, &s->hdr_info.client_request);
+    if (!(is_request_valid(s, &s->hdr_info.client_request))) {
+      HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
+      TxnDebug("http_seq", "[HttpTransact::HandleRequest] request invalid.");
+      s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
+      //  s->next_action = HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
+      return;
+    }
+    TxnDebug("http_seq", "[HttpTransact::HandleRequest] request valid.");
+
+    if (is_debug_tag_set("http_chdr_describe")) {
+      obj_describe(s->hdr_info.client_request.m_http, true);
+    }
+    // at this point we are guaranteed that the request is good and acceptable.
+    // initialize some state variables from the request (client version,
+    // client keep-alive, cache action, etc.
+    initialize_state_variables_from_request(s, &s->hdr_info.client_request);
+    // The following chunk of code will limit the maximum number of websocket connections
(TS-3659)
+    if (s->is_upgrade_request && s->is_websocket && s->http_config_param->max_websocket_connections
>= 0) {
+      int64_t val = 0;
+      HTTP_READ_DYN_SUM(http_websocket_current_active_client_connections_stat, val);
+      if (val >= s->http_config_param->max_websocket_connections) {
+        s->is_websocket = false; // unset to avoid screwing up stats.
+        TxnDebug("http_trans", "Rejecting websocket connection because the limit has been
exceeded");
+        bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
+        build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "WebSocket Connection Limit
Exceeded", nullptr);
+        TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
+      }
+    }
 
-  // The following chunk of code will limit the maximum number of websocket connections (TS-3659)
-  if (s->is_upgrade_request && s->is_websocket && s->http_config_param->max_websocket_connections
>= 0) {
-    int64_t val = 0;
-    HTTP_READ_DYN_SUM(http_websocket_current_active_client_connections_stat, val);
-    if (val >= s->http_config_param->max_websocket_connections) {
-      s->is_websocket = false; // unset to avoid screwing up stats.
-      TxnDebug("http_trans", "Rejecting websocket connection because the limit has been exceeded");
+    // The following code is configurable to allow a user to control the max post size (TS-3631)
+    if (s->http_config_param->max_post_size > 0 && s->hdr_info.request_content_length
> 0 &&
+        s->hdr_info.request_content_length > s->http_config_param->max_post_size)
{
+      TxnDebug("http_trans", "Max post size %" PRId64 " Client tried to post a body that
was too large.",
+               s->http_config_param->max_post_size);
+      HTTP_INCREMENT_DYN_STAT(http_post_body_too_large);
       bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
-      build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "WebSocket Connection Limit
Exceeded", nullptr);
+      build_error_response(s, HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large",
"request#entity_too_large");
+      s->squid_codes.log_code = SQUID_LOG_ERR_POST_ENTITY_TOO_LARGE;
       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
     }
-  }
-
-  // The following code is configurable to allow a user to control the max post size (TS-3631)
-  if (s->http_config_param->max_post_size > 0 && s->hdr_info.request_content_length
> 0 &&
-      s->hdr_info.request_content_length > s->http_config_param->max_post_size)
{
-    TxnDebug("http_trans", "Max post size %" PRId64 " Client tried to post a body that was
too large.",
-             s->http_config_param->max_post_size);
-    HTTP_INCREMENT_DYN_STAT(http_post_body_too_large);
-    bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
-    build_error_response(s, HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large",
"request#entity_too_large");
-    s->squid_codes.log_code = SQUID_LOG_ERR_POST_ENTITY_TOO_LARGE;
-    TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
-  }
 
-  // The following chunk of code allows you to disallow post w/ expect 100-continue (TS-3459)
-  if (s->hdr_info.request_content_length && s->http_config_param->disallow_post_100_continue)
{
-    MIMEField *expect = s->hdr_info.client_request.field_find(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
-
-    if (expect != nullptr) {
-      const char *expect_hdr_val = nullptr;
-      int expect_hdr_val_len     = 0;
-      expect_hdr_val             = expect->value_get(&expect_hdr_val_len);
-      if (ptr_len_casecmp(expect_hdr_val, expect_hdr_val_len, HTTP_VALUE_100_CONTINUE, HTTP_LEN_100_CONTINUE)
== 0) {
-        // Let's error out this request.
-        TxnDebug("http_trans", "Client sent a post expect: 100-continue, sending 405.");
-        HTTP_INCREMENT_DYN_STAT(disallowed_post_100_continue);
-        build_error_response(s, HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed", "request#method_unsupported");
-        TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
+    // The following chunk of code allows you to disallow post w/ expect 100-continue (TS-3459)
+    if (s->hdr_info.request_content_length && s->http_config_param->disallow_post_100_continue)
{
+      MIMEField *expect = s->hdr_info.client_request.field_find(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
+
+      if (expect != nullptr) {
+        const char *expect_hdr_val = nullptr;
+        int expect_hdr_val_len     = 0;
+        expect_hdr_val             = expect->value_get(&expect_hdr_val_len);
+        if (ptr_len_casecmp(expect_hdr_val, expect_hdr_val_len, HTTP_VALUE_100_CONTINUE,
HTTP_LEN_100_CONTINUE) == 0) {
+          // Let's error out this request.
+          TxnDebug("http_trans", "Client sent a post expect: 100-continue, sending 405.");
+          HTTP_INCREMENT_DYN_STAT(disallowed_post_100_continue);
+          build_error_response(s, HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed", "request#method_unsupported");
+          TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
+        }
       }
     }
+    if (s->txn_conf->request_buffer_enabled &&
+        (s->hdr_info.request_content_length > 0 || s->client_info.transfer_encoding
== CHUNKED_ENCODING)) {
+      TRANSACT_RETURN(SM_ACTION_WAIT_FOR_FULL_BODY, nullptr);
+    }
   }
 
   // Cache lookup or not will be decided later at DecideCacheLookup().
@@ -1307,6 +1311,12 @@ HttpTransact::HandleRequest(State *s)
 }
 
 void
+HttpTransact::HandleRequestBufferDone(State *s)
+{
+  TRANSACT_RETURN(SM_ACTION_REQUEST_BUFFER_READ_COMPLETE, HttpTransact::HandleRequest);
+}
+
+void
 HttpTransact::setup_plugin_request_intercept(State *s)
 {
   ink_assert(s->state_machine->plugin_tunnel != nullptr);
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 3b09a71..3d30058 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -425,6 +425,8 @@ public:
     SM_ACTION_DRAIN_REQUEST_BODY,
 #endif /* PROXY_DRAIN */
 
+    SM_ACTION_WAIT_FOR_FULL_BODY,
+    SM_ACTION_REQUEST_BUFFER_READ_COMPLETE,
     SM_ACTION_SERVE_FROM_CACHE,
     SM_ACTION_SERVER_READ,
     SM_ACTION_SERVER_PARSE_NEXT_HDR,
@@ -957,6 +959,7 @@ public:
   static void PerformRemap(State *s);
   static void ModifyRequest(State *s);
   static void HandleRequest(State *s);
+  static void HandleRequestBufferDone(State *s);
   static bool handleIfRedirect(State *s);
 
   static void StartAccessControl(State *s);
diff --git a/proxy/http/HttpTunnel.cc b/proxy/http/HttpTunnel.cc
index c5f1fa5..606e1b0 100644
--- a/proxy/http/HttpTunnel.cc
+++ b/proxy/http/HttpTunnel.cc
@@ -757,7 +757,6 @@ void
 HttpTunnel::tunnel_run(HttpTunnelProducer *p_arg)
 {
   Debug("http_tunnel", "tunnel_run started, p_arg is %s", p_arg ? "provided" : "NULL");
-
   if (p_arg) {
     producer_run(p_arg);
   } else {
@@ -876,7 +875,6 @@ HttpTunnel::producer_run(HttpTunnelProducer *p)
   // Do the IO on the consumers first so
   //  data doesn't disappear out from
   //  under the tunnel
-  ink_release_assert(p->num_consumers > 0);
   for (c = p->consumer_list.head; c;) {
     // Create a reader for each consumer.  The reader allows
     // us to implement skip bytes
@@ -952,15 +950,20 @@ HttpTunnel::producer_run(HttpTunnelProducer *p)
   // YTS Team, yamsat Plugin
   // Allocate and copy partial POST data to buffers. Check for the various parameters
   // including the maximum configured post data size
-  if (p->alive && sm->t_state.method == HTTP_WKSIDX_POST && sm->enable_redirection
&& (p->vc_type == HT_HTTP_CLIENT)) {
+  if ((p->vc_type == HT_BUFFER_READ && sm->is_postbuf_valid()) ||
+      (p->alive && sm->t_state.method == HTTP_WKSIDX_POST && sm->enable_redirection
&& p->vc_type == HT_HTTP_CLIENT)) {
     Debug("http_redirect", "[HttpTunnel::producer_run] client post: %" PRId64 " max size:
%" PRId64 "",
           p->buffer_start->read_avail(), HttpConfig::m_master.post_copy_size);
 
     // (note that since we are not dechunking POST, this is the chunked size if chunked)
     if (p->buffer_start->read_avail() > HttpConfig::m_master.post_copy_size) {
-      Debug("http_redirect", "[HttpTunnel::producer_handler] post exceeds buffer limit, buffer_avail=%"
PRId64 " limit=%" PRId64 "",
-            p->buffer_start->read_avail(), HttpConfig::m_master.post_copy_size);
+      Warning("http_redirect, [HttpTunnel::producer_handler] post exceeds buffer limit, buffer_avail=%"
PRId64 " limit=%" PRId64 "",
+              p->buffer_start->read_avail(), HttpConfig::m_master.post_copy_size);
       sm->disable_redirect();
+      if (p->vc_type == HT_BUFFER_READ) {
+        producer_handler(VC_EVENT_ERROR, p);
+        return;
+      }
     } else {
       sm->postbuf_copy_partial_data();
     }
@@ -993,8 +996,7 @@ HttpTunnel::producer_run(HttpTunnelProducer *p)
     // p->chunked_handler.skip_bytes);
 
     producer_handler(VC_EVENT_READ_READY, p);
-    if (!p->chunked_handler.chunked_reader->read_avail() && sm->redirection_tries
> 0 &&
-        p->vc_type == HT_HTTP_CLIENT) { // read_avail() == 0
+    if (sm->get_postbuf_done() && p->vc_type == HT_HTTP_CLIENT) { // read_avail()
== 0
       // [bug 2579251]
       // Ugh, this is horrible but in the redirect case they are running a the tunnel again
with the
       // now closed/empty producer to trigger PRECOMPLETE.  If the POST was chunked, producer_n
is set
@@ -1162,17 +1164,24 @@ HttpTunnel::producer_handler(int event, HttpTunnelProducer *p)
   // YTS Team, yamsat Plugin
   // Copy partial POST data to buffers. Check for the various parameters including
   // the maximum configured post data size
-  if (sm->t_state.method == HTTP_WKSIDX_POST && sm->enable_redirection &&
-      (event == VC_EVENT_READ_READY || event == VC_EVENT_READ_COMPLETE) && (p->vc_type
== HT_HTTP_CLIENT)) {
+  if ((p->vc_type == HT_BUFFER_READ && sm->is_postbuf_valid()) ||
+      (sm->t_state.method == HTTP_WKSIDX_POST && sm->enable_redirection &&
+       (event == VC_EVENT_READ_READY || event == VC_EVENT_READ_COMPLETE) && p->vc_type
== HT_HTTP_CLIENT)) {
     Debug("http_redirect", "[HttpTunnel::producer_handler] [%s %s]", p->name, HttpDebugNames::get_event_name(event));
 
     if ((sm->postbuf_buffer_avail() + sm->postbuf_reader_avail()) > HttpConfig::m_master.post_copy_size)
{
-      Debug("http_redirect", "[HttpTunnel::producer_handler] post exceeds buffer limit, buffer_avail=%"
PRId64
-                             " reader_avail=%" PRId64 " limit=%" PRId64 "",
-            sm->postbuf_buffer_avail(), sm->postbuf_reader_avail(), HttpConfig::m_master.post_copy_size);
+      Warning("http_redirect, [HttpTunnel::producer_handler] post exceeds buffer limit, buffer_avail=%"
PRId64
+              " reader_avail=%" PRId64 " limit=%" PRId64 "",
+              sm->postbuf_buffer_avail(), sm->postbuf_reader_avail(), HttpConfig::m_master.post_copy_size);
       sm->disable_redirect();
+      if (p->vc_type == HT_BUFFER_READ) {
+        event = VC_EVENT_ERROR;
+      }
     } else {
       sm->postbuf_copy_partial_data();
+      if (event == VC_EVENT_READ_COMPLETE || event == HTTP_TUNNEL_EVENT_PRECOMPLETE || event
== VC_EVENT_EOS) {
+        sm->set_postbuf_done(true);
+      }
     }
   } // end of added logic for partial copy of POST
 
diff --git a/proxy/http/HttpTunnel.h b/proxy/http/HttpTunnel.h
index d98c32d..ca15219 100644
--- a/proxy/http/HttpTunnel.h
+++ b/proxy/http/HttpTunnel.h
@@ -66,14 +66,7 @@ struct HttpTunnelProducer;
 typedef int (HttpSM::*HttpProducerHandler)(int event, HttpTunnelProducer *p);
 typedef int (HttpSM::*HttpConsumerHandler)(int event, HttpTunnelConsumer *c);
 
-enum HttpTunnelType_t {
-  HT_HTTP_SERVER,
-  HT_HTTP_CLIENT,
-  HT_CACHE_READ,
-  HT_CACHE_WRITE,
-  HT_TRANSFORM,
-  HT_STATIC,
-};
+enum HttpTunnelType_t { HT_HTTP_SERVER, HT_HTTP_CLIENT, HT_CACHE_READ, HT_CACHE_WRITE, HT_TRANSFORM,
HT_STATIC, HT_BUFFER_READ };
 
 enum TunnelChunkingAction_t {
   TCA_CHUNK_CONTENT,

-- 
To stop receiving notification emails like this one, please contact
bcall@apache.org.

Mime
View raw message