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 2020/01/17 20:17:08 UTC

[trafficserver] branch master updated: Fix problems with "Probe" option for X-Debug MIME header field. (#6197)

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


The following commit(s) were added to refs/heads/master by this push:
     new 473e054  Fix problems with "Probe" option for X-Debug MIME header field. (#6197)
473e054 is described below

commit 473e0549cbb7bda763340b842d447d9db0d49879
Author: Walt Karas <wk...@verizonmedia.com>
AuthorDate: Fri Jan 17 14:16:57 2020 -0600

    Fix problems with "Probe" option for X-Debug MIME header field. (#6197)
    
    Fix problems with "Probe" option for X-Debug MIME header field.
    - Adds exercise of "Probe" to the existing x_remap Au test.
    - Remove memory leaks and risk of double deletion in xdebug plugin.
---
 plugins/xdebug/Cleanup.h                           | 186 ++++++++++++
 plugins/xdebug/Makefile.inc                        |   4 +-
 plugins/xdebug/xdebug.cc                           |  69 +++--
 plugins/xdebug/xdebug_transforms.cc                |  44 +--
 tests/gold_tests/pluginTest/xdebug/x_remap/four.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/fwd1.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/fwd2.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/fwd3.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/fwd4.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/fwd5.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/none.in |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/one.in  |   3 +-
 .../gold_tests/pluginTest/xdebug/x_remap/out.gold  | 315 ++++++++++++++++++++-
 .../gold_tests/pluginTest/xdebug/x_remap/three.in  |   3 +-
 tests/gold_tests/pluginTest/xdebug/x_remap/two.in  |   3 +-
 .../pluginTest/xdebug/x_remap/x_remap.gold         |   6 +-
 .../pluginTest/xdebug/x_remap/x_remap.test.py      |   4 +-
 17 files changed, 577 insertions(+), 81 deletions(-)

diff --git a/plugins/xdebug/Cleanup.h b/plugins/xdebug/Cleanup.h
new file mode 100644
index 0000000..453903c
--- /dev/null
+++ b/plugins/xdebug/Cleanup.h
@@ -0,0 +1,186 @@
+/**
+  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.
+ */
+
+/**
+ * @file Cleanup.h
+ * @brief Easy-to-use utilities to avoid resource leaks or double-releases of resources.  Independent of the rest
+ *        of the CPPAPI.
+ */
+
+#pragma once
+
+#include <type_traits>
+#include <memory>
+
+#include <ts/ts.h>
+
+namespace atscppapi
+{
+// For TS API types TSXxx with a TSXxxDestroy function, define standard deleter TSXxxDeleter, and use it to
+// define TSXxxUniqPtr (specialization of std::unique_ptr).  X() is used when the destroy function returns void,
+// Y() is used when the destroy function returns TSReturnCode.
+
+#if defined(X)
+#error "X defined as preprocessor symbol"
+#endif
+
+#define X(NAME_SEGMENT)               \
+  struct TS##NAME_SEGMENT##Deleter {  \
+    void                              \
+    operator()(TS##NAME_SEGMENT ptr)  \
+    {                                 \
+      TS##NAME_SEGMENT##Destroy(ptr); \
+    }                                 \
+  };                                  \
+  using TS##NAME_SEGMENT##UniqPtr = std::unique_ptr<std::remove_pointer_t<TS##NAME_SEGMENT>, TS##NAME_SEGMENT##Deleter>;
+
+#if defined(Y)
+#error "Y defined as preprocessor symbol"
+#endif
+
+#define Y(NAME_SEGMENT)                                       \
+  struct TS##NAME_SEGMENT##Deleter {                          \
+    void                                                      \
+    operator()(TS##NAME_SEGMENT ptr)                          \
+    {                                                         \
+      TSAssert(TS##NAME_SEGMENT##Destroy(ptr) == TS_SUCCESS); \
+    }                                                         \
+  };                                                          \
+  using TS##NAME_SEGMENT##UniqPtr = std::unique_ptr<std::remove_pointer_t<TS##NAME_SEGMENT>, TS##NAME_SEGMENT##Deleter>;
+
+Y(MBuffer)       // Defines TSMBufferDeleter and TSMBufferUniqPtr.
+X(MimeParser)    // Defines TSMimeParserDeleter and TSMimeParserUniqPtr.
+X(Thread)        // Defines TSThreadDeleter and TSThreadUniqPtr.
+X(Mutex)         // Defines TSMutexDeleter and TSMutexUniqPtr.
+Y(CacheKey)      // Defines TSCacheKeyDeleter and TSCacheKeyUniqPtr.
+X(Cont)          // Defines TSContDeleter and TSContUniqPtr.
+X(SslContext)    // Defines TSSslContextDeleter and TSSslContextUniqPtr.
+X(IOBuffer)      // Defines TSIOBufferDeleter and TSIOBufferUniqPtr.
+Y(TextLogObject) // Defines TSTextLogObjectDeleter and TSTextLogObjectUniqPtr.
+X(Uuid)          // Defines TSUuidDeleter and TSUuidUniqPtr.
+
+#undef X
+#undef Y
+
+// Deleter and unique pointer for memory buffer returned by TSalloc(), TSrealloc(), Tstrdup(), TSsrtndup().
+//
+struct TSMemDeleter {
+  void
+  operator()(void *ptr)
+  {
+    TSfree(ptr);
+  }
+};
+using TSMemUniqPtr = std::unique_ptr<void, TSMemDeleter>;
+
+// Deleter and unique pointer for TSIOBufferReader.  Care must be taken that the reader is deleted before the
+// TSIOBuffer to which it refers is deleted.
+//
+struct TSIOBufferReaderDeleter {
+  void
+  operator()(TSIOBufferReader ptr)
+  {
+    TSIOBufferReaderFree(ptr);
+  }
+};
+using TSIOBufferReaderUniqPtr = std::unique_ptr<std::remove_pointer_t<TSIOBufferReader>, TSIOBufferReaderDeleter>;
+
+class TxnAuxDataMgrBase
+{
+protected:
+  struct MgrData_ {
+    TSCont txnCloseContp = nullptr;
+    int txnArgIndex      = -1;
+  };
+
+public:
+  class MgrData : private MgrData_
+  {
+    friend class TxnAuxDataMgrBase;
+  };
+
+protected:
+  static MgrData_ &
+  access(MgrData &md)
+  {
+    return md;
+  }
+};
+
+using TxnAuxMgrData = TxnAuxDataMgrBase::MgrData;
+
+// Class to manage auxilliary data for a transaction.  If an instance is created for the transaction, the instance
+// will be deleted on the TXN_CLOSE transaction hook (which is always triggered for all transactions).
+// The TxnAuxData class must have a public default constructor.
+//
+template <class TxnAuxData, TxnAuxMgrData &MDRef> class TxnAuxDataMgr : private TxnAuxDataMgrBase
+{
+public:
+  using Data = TxnAuxData;
+
+  // This must be called from the plugin init function.  arg_name is the name for the transaction argument used
+  // to store the pointer to the auxiliary data class instance.  Repeated calls are ignored.
+  //
+  static void
+  init(char const *arg_name, char const *arg_desc = "per-transaction auxiliary data")
+  {
+    MgrData_ &md = access(MDRef);
+
+    if (md.txnArgIndex >= 0) {
+      return;
+    }
+
+    TSReleaseAssert(TSHttpTxnArgIndexReserve(arg_name, arg_desc, &md.txnArgIndex) == TS_SUCCESS);
+    TSReleaseAssert(md.txnCloseContp = TSContCreate(_deleteAuxData, nullptr));
+  }
+
+  // Get a reference to the auxiliary data for a transaction.
+  //
+  static TxnAuxData &
+  data(TSHttpTxn txn)
+  {
+    MgrData_ &md = access(MDRef);
+
+    TSAssert(md.txnArgIndex >= 0);
+
+    auto d = static_cast<TxnAuxData *>(TSHttpTxnArgGet(txn, md.txnArgIndex));
+    if (!d) {
+      d = new TxnAuxData;
+
+      TSHttpTxnArgSet(txn, md.txnArgIndex, d);
+
+      TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, md.txnCloseContp);
+    }
+    return *d;
+  }
+
+private:
+  static int
+  _deleteAuxData(TSCont, TSEvent, void *edata)
+  {
+    MgrData_ &md = access(MDRef);
+
+    auto txn  = static_cast<TSHttpTxn>(edata);
+    auto data = static_cast<TxnAuxData *>(TSHttpTxnArgGet(txn, md.txnArgIndex));
+    delete data;
+    TSHttpTxnReenable(txn, TS_EVENT_HTTP_CONTINUE);
+    return 0;
+  };
+};
+
+} // end namespace atscppapi
diff --git a/plugins/xdebug/Makefile.inc b/plugins/xdebug/Makefile.inc
index 1e41324..a69e223 100644
--- a/plugins/xdebug/Makefile.inc
+++ b/plugins/xdebug/Makefile.inc
@@ -15,4 +15,6 @@
 #  limitations under the License.
 
 pkglib_LTLIBRARIES += xdebug/xdebug.la
-xdebug_xdebug_la_SOURCES = xdebug/xdebug.cc
+xdebug_xdebug_la_SOURCES = \
+xdebug/Cleanup.h \
+xdebug/xdebug.cc
diff --git a/plugins/xdebug/xdebug.cc b/plugins/xdebug/xdebug.cc
index a3a2443..3493575 100644
--- a/plugins/xdebug/xdebug.cc
+++ b/plugins/xdebug/xdebug.cc
@@ -22,6 +22,8 @@
 #include <strings.h>
 #include <sstream>
 #include <cstring>
+#include <atomic>
+#include <memory>
 #include <getopt.h>
 #include <cstdint>
 #include <cinttypes>
@@ -32,6 +34,34 @@
 #include "tscore/ink_defs.h"
 #include "tscpp/util/PostScript.h"
 #include "tscpp/util/TextView.h"
+#include "Cleanup.h"
+
+namespace
+{
+struct BodyBuilder {
+  atscppapi::TSContUniqPtr transform_connp;
+  atscppapi::TSIOBufferUniqPtr output_buffer;
+  // It's important that output_reader comes after output_buffer so it will be deleted first.
+  atscppapi::TSIOBufferReaderUniqPtr output_reader;
+  TSVIO output_vio   = nullptr;
+  bool wrote_prebody = false;
+  bool wrote_body    = false;
+  bool hdr_ready     = false;
+  std::atomic_flag wrote_postbody;
+
+  int64_t nbytes = 0;
+};
+
+struct XDebugTxnAuxData {
+  std::unique_ptr<BodyBuilder> body_builder;
+  unsigned xheaders = 0;
+};
+
+atscppapi::TxnAuxMgrData mgrData;
+
+using AuxDataMgr = atscppapi::TxnAuxDataMgr<XDebugTxnAuxData, mgrData>;
+
+} // end anonymous namespace
 
 #include "xdebug_headers.cc"
 #include "xdebug_transforms.cc"
@@ -53,8 +83,6 @@ enum {
   XHEADER_X_PSELECT_KEY    = 1u << 10,
 };
 
-static int XArgIndex              = 0;
-static int BodyBuilderArgIndex    = 0;
 static TSCont XInjectHeadersCont  = nullptr;
 static TSCont XDeleteDebugHdrCont = nullptr;
 
@@ -382,7 +410,7 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata)
 
   TSReleaseAssert(event == TS_EVENT_HTTP_SEND_RESPONSE_HDR);
 
-  uintptr_t xheaders = reinterpret_cast<uintptr_t>(TSHttpTxnArgGet(txn, XArgIndex));
+  unsigned xheaders = AuxDataMgr::data(txn).xheaders;
   if (xheaders == 0) {
     goto done;
   }
@@ -422,14 +450,14 @@ XInjectResponseHeaders(TSCont /* contp */, TSEvent event, void *edata)
   }
 
   if (xheaders & XHEADER_X_PROBE_HEADERS) {
-    BodyBuilder *data = static_cast<BodyBuilder *>(TSHttpTxnArgGet(txn, BodyBuilderArgIndex));
+    BodyBuilder *data = AuxDataMgr::data(txn).body_builder.get();
     TSDebug("xdebug_transform", "XInjectResponseHeaders(): client resp header ready");
     if (data == nullptr) {
       TSHttpTxnReenable(txn, TS_EVENT_HTTP_ERROR);
       return TS_ERROR;
     }
     data->hdr_ready = true;
-    writePostBody(data);
+    writePostBody(txn, data);
   }
 
   if (xheaders & XHEADER_X_PSELECT_KEY) {
@@ -493,9 +521,9 @@ isFwdFieldValue(std::string_view value, intmax_t &fwdCnt)
 static int
 XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
 {
-  TSHttpTxn txn      = static_cast<TSHttpTxn>(edata);
-  uintptr_t xheaders = 0;
-  intmax_t fwdCnt    = 0;
+  TSHttpTxn txn     = static_cast<TSHttpTxn>(edata);
+  unsigned xheaders = 0;
+  intmax_t fwdCnt   = 0;
   TSMLoc field, next;
   TSMBuffer buffer;
   TSMLoc hdr;
@@ -550,26 +578,17 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
       } else if (header_field_eq("probe", value, vsize)) {
         xheaders |= XHEADER_X_PROBE_HEADERS;
 
+        auto &auxData = AuxDataMgr::data(txn);
+
         // prefix request headers and postfix response headers
         BodyBuilder *data = new BodyBuilder();
-        data->txn         = txn;
+        auxData.body_builder.reset(data);
 
         TSVConn connp = TSTransformCreate(body_transform, txn);
-        TSContDataSet(connp, data);
+        data->transform_connp.reset(connp);
+        TSContDataSet(connp, txn);
         TSHttpTxnHookAdd(txn, TS_HTTP_RESPONSE_TRANSFORM_HOOK, connp);
 
-        // store data pointer in txnarg to use in global cont XInjectResponseHeaders
-        TSHttpTxnArgSet(txn, BodyBuilderArgIndex, data);
-
-        // create a self-cleanup on close
-        auto cleanupBodyBuilder = [](TSCont /* contp */, TSEvent event, void *edata) -> int {
-          TSHttpTxn txn     = static_cast<TSHttpTxn>(edata);
-          BodyBuilder *data = static_cast<BodyBuilder *>(TSHttpTxnArgGet(txn, BodyBuilderArgIndex));
-          delete data;
-          return TS_EVENT_NONE;
-        };
-        TSHttpTxnHookAdd(txn, TS_HTTP_TXN_CLOSE_HOOK, TSContCreate(cleanupBodyBuilder, nullptr));
-
         // disable writing to cache because we are injecting data into the body.
         TSHttpTxnReqCacheableSet(txn, 0);
         TSHttpTxnRespCacheableSet(txn, 0);
@@ -608,7 +627,7 @@ XScanRequestHeaders(TSCont /* contp */, TSEvent event, void *edata)
     TSDebug("xdebug", "adding response hook for header mask %p and forward count %" PRIiMAX, reinterpret_cast<void *>(xheaders),
             fwdCnt);
     TSHttpTxnHookAdd(txn, TS_HTTP_SEND_RESPONSE_HDR_HOOK, XInjectHeadersCont);
-    TSHttpTxnArgSet(txn, XArgIndex, reinterpret_cast<void *>(xheaders));
+    AuxDataMgr::data(txn).xheaders = xheaders;
 
     if (fwdCnt == 0) {
       // X-Debug header has to be deleted, but not too soon for other plugins to see it.
@@ -688,9 +707,9 @@ TSPluginInit(int argc, const char *argv[])
   }
   xDebugHeader.len = strlen(xDebugHeader.str);
 
+  AuxDataMgr::init("xdebug");
+
   // Setup the global hook
-  TSReleaseAssert(TSHttpTxnArgIndexReserve("xdebug", "xdebug header requests", &XArgIndex) == TS_SUCCESS);
-  TSReleaseAssert(TSHttpTxnArgIndexReserve("bodyTransform", "BodyBuilder*", &XArgIndex) == TS_SUCCESS);
   TSReleaseAssert(XInjectHeadersCont = TSContCreate(XInjectResponseHeaders, nullptr));
   TSReleaseAssert(XDeleteDebugHdrCont = TSContCreate(XDeleteDebugHdr, nullptr));
   TSHttpHookAdd(TS_HTTP_READ_REQUEST_HDR_HOOK, TSContCreate(XScanRequestHeaders, nullptr));
diff --git a/plugins/xdebug/xdebug_transforms.cc b/plugins/xdebug/xdebug_transforms.cc
index a5bc75a..e1019e7 100644
--- a/plugins/xdebug/xdebug_transforms.cc
+++ b/plugins/xdebug/xdebug_transforms.cc
@@ -21,25 +21,11 @@
 #include <stdio.h>
 #include <string.h>
 #include <functional>
-#include <atomic>
 
 #include "ts/ts.h"
 
 static const std::string_view MultipartBoundary{"\r\n--- ATS xDebug Probe Injection Boundary ---\r\n\r\n"};
 
-struct BodyBuilder {
-  TSVIO output_vio               = nullptr;
-  TSIOBuffer output_buffer       = nullptr;
-  TSIOBufferReader output_reader = nullptr;
-  bool wrote_prebody             = false;
-  bool wrote_body                = false;
-  bool hdr_ready                 = false;
-  std::atomic_flag wrote_postbody;
-
-  int64_t nbytes = 0;
-  TSHttpTxn txn  = nullptr;
-};
-
 static char Hostname[1024];
 
 static std::string
@@ -65,12 +51,12 @@ getPostBody(TSHttpTxn txn)
 }
 
 static void
-writePostBody(BodyBuilder *data)
+writePostBody(TSHttpTxn txn, BodyBuilder *data)
 {
   if (data->wrote_body && data->hdr_ready && !data->wrote_postbody.test_and_set()) {
     TSDebug("xdebug_transform", "body_transform(): Writing postbody headers...");
-    std::string postbody = getPostBody(data->txn);
-    TSIOBufferWrite(data->output_buffer, postbody.data(), postbody.length());
+    std::string postbody = getPostBody(txn);
+    TSIOBufferWrite(data->output_buffer.get(), postbody.data(), postbody.length());
     data->nbytes += postbody.length();
     TSVIONBytesSet(data->output_vio, data->nbytes);
     TSVIOReenable(data->output_vio);
@@ -80,15 +66,13 @@ writePostBody(BodyBuilder *data)
 static int
 body_transform(TSCont contp, TSEvent event, void *edata)
 {
-  BodyBuilder *data = static_cast<BodyBuilder *>(TSContDataGet(contp));
+  TSHttpTxn txn     = static_cast<TSHttpTxn>(TSContDataGet(contp));
+  BodyBuilder *data = AuxDataMgr::data(txn).body_builder.get();
   if (!data) {
-    TSContDestroy(contp);
     return TS_ERROR;
   }
   if (TSVConnClosedGet(contp)) {
-    // write connection destroyed. cleanup.
-    delete data;
-    TSContDestroy(contp);
+    // write connection destroyed.
     return 0;
   }
 
@@ -108,16 +92,16 @@ body_transform(TSCont contp, TSEvent event, void *edata)
     TSDebug("xdebug_transform", "body_transform(): Event is TS_EVENT_VCONN_WRITE_READY");
   // fall through
   default:
-    if (!data->output_buffer) {
-      data->output_buffer = TSIOBufferCreate();
-      data->output_reader = TSIOBufferReaderAlloc(data->output_buffer);
-      data->output_vio    = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, data->output_reader, INT64_MAX);
+    if (!data->output_buffer.get()) {
+      data->output_buffer.reset(TSIOBufferCreate());
+      data->output_reader.reset(TSIOBufferReaderAlloc(data->output_buffer.get()));
+      data->output_vio = TSVConnWrite(TSTransformOutputVConnGet(contp), contp, data->output_reader.get(), INT64_MAX);
     }
 
     if (data->wrote_prebody == false) {
       TSDebug("xdebug_transform", "body_transform(): Writing prebody headers...");
-      std::string prebody = getPreBody(data->txn);
-      TSIOBufferWrite(data->output_buffer, prebody.data(), prebody.length()); // write prebody
+      std::string prebody = getPreBody(txn);
+      TSIOBufferWrite(data->output_buffer.get(), prebody.data(), prebody.length()); // write prebody
       data->wrote_prebody = true;
       data->nbytes += prebody.length();
     }
@@ -127,7 +111,7 @@ body_transform(TSCont contp, TSEvent event, void *edata)
     if (!src_buf) {
       // upstream continuation shuts down write operation.
       data->wrote_body = true;
-      writePostBody(data);
+      writePostBody(txn, data);
       return 0;
     }
 
@@ -150,7 +134,7 @@ body_transform(TSCont contp, TSEvent event, void *edata)
       // Write post body content and update output VIO
       data->wrote_body = true;
       data->nbytes += TSVIONDoneGet(src_vio);
-      writePostBody(data);
+      writePostBody(txn, data);
       TSContCall(TSVIOContGet(src_vio), TS_EVENT_VCONN_WRITE_COMPLETE, src_vio);
     }
   }
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/four.in b/tests/gold_tests/pluginTest/xdebug/x_remap/four.in
index 1982451..6842039 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/four.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/four.in
@@ -1,4 +1,5 @@
 GET /not_there HTTP/1.1
 Host: two
-X-Debug: X-Remap
+X-Debug: X-Remap, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd1.in b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd1.in
index c67c8ea..82f3fa5 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd1.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd1.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap, fwd
+X-Debug: X-Remap, fwd, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd2.in b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd2.in
index 8cb9e7c..4f7ea91 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd2.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd2.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap, fwd=0
+X-Debug: X-Remap, fwd=0, probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd3.in b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd3.in
index 47402f2..42c187a 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd3.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd3.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap, fwd= 1
+X-Debug: X-Remap, fwd= 1, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd4.in b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd4.in
index 75ca74b..db3ce25 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd4.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd4.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap, fwd = 999999
+X-Debug: PROBE, X-Remap, fwd = 999999
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd5.in b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd5.in
index 3b3e9af..02b44e7 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/fwd5.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/fwd5.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap, fwd = 999999xxx
+X-Debug: X-Remap, fwd = 999999xxx, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/none.in b/tests/gold_tests/pluginTest/xdebug/x_remap/none.in
index 6e5b719..a077b72 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/none.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/none.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: none
-X-Debug: X-Remap
+X-Debug: X-Remap, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/one.in b/tests/gold_tests/pluginTest/xdebug/x_remap/one.in
index 48306be..6a35a87 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/one.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/one.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: one
-X-Debug: X-Remap
+X-Debug: X-Remap, probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/out.gold b/tests/gold_tests/pluginTest/xdebug/x_remap/out.gold
index 0619bd2..4b1e265 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/out.gold
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/out.gold
@@ -1,6 +1,6 @@
 HTTP/1.1 502 Cannot find server.
 Date: ``
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 Cache-Control: no-store
 Content-Type: text/html
@@ -30,10 +30,42 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://one/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://one/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -41,10 +73,42 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -52,10 +116,42 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://three[0-9]+/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://three[0-9]+/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -64,9 +160,42 @@ Server: ATS/``
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Server' : 'MicroServer',
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Server' : 'ATS/``
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -74,10 +203,44 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'X-Remap, fwd, Probe',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'X-Remap, fwd, Probe',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -85,10 +248,42 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -96,10 +291,44 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'X-Remap, fwd=0, Probe',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'X-Remap, fwd=0, Probe',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -107,10 +336,44 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'PROBE, X-Remap, fwd=999998',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'X-Debug' : 'PROBE, X-Remap, fwd=999998',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
@@ -118,10 +381,42 @@ HTTP/1.1 200 OK
 Date: ``
 Age: ``
 Transfer-Encoding: chunked
-Connection: keep-alive
+Connection: close
 Server: ATS/``
 X-Remap: from=http://two/, to=http://127.0.0.1:SERVER_PORT/
 
+``
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'request', 'side':'client', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Connection' : 'close',
+	}},{'type':'request', 'side':'server', 'headers': {
+	'Host' : '127.0.0.1:SERVER_PORT',
+	'Client-ip' : '127.0.0.1',
+	'X-Forwarded-For' : '127.0.0.1',
+	'Via' : 'http/1.1 traffic_server[``
+	}}
+   ]
+}
+--- ATS xDebug Probe Injection Boundary ---
+
+
+--- ATS xDebug Probe Injection Boundary ---
+
+{'xDebugProbeAt' : '``
+   'captured':[{'type':'response', 'side':'server', 'headers': {
+	'Connection' : 'close',
+	'Date' : '``
+	}},{'type':'response', 'side':'client', 'headers': {
+	'Date' : '``
+	'Age' : '0',
+	'Transfer-Encoding' : 'chunked',
+	'Connection' : 'close',
+	'Server' : 'ATS/``
+	'X-Remap' : 'from=http://two/, to=http://127.0.0.1:SERVER_PORT/',
+	}}
+   ]
+}
 0
 
 ======
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/three.in b/tests/gold_tests/pluginTest/xdebug/x_remap/three.in
index b525bf8..1d9612a 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/three.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/three.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: three123
-X-Debug: X-Remap
+X-Debug: X-Remap, Probe
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/two.in b/tests/gold_tests/pluginTest/xdebug/x_remap/two.in
index c971a91..514d173 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/two.in
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/two.in
@@ -1,4 +1,5 @@
 GET /argh HTTP/1.1
 Host: two
-X-Debug: X-Remap
+X-Debug: PROBE, X-Remap
+Connection: close
 
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.gold b/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.gold
index d0baa81..5a1e2fd 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.gold
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.gold
@@ -6,13 +6,13 @@ X_DEBUG MISSING
 -
 X_DEBUG MISSING
 -
-X-Remap, fwd
+X-Remap, fwd, Probe
 -
 X_DEBUG MISSING
 -
-X-Remap, fwd=0
+X-Remap, fwd=0, Probe
 -
-X-Remap, fwd=999998
+PROBE, X-Remap, fwd=999998
 -
 X_DEBUG MISSING
 -
diff --git a/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.test.py b/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.test.py
index 89db82a..73773d8 100644
--- a/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.test.py
+++ b/tests/gold_tests/pluginTest/xdebug/x_remap/x_remap.test.py
@@ -15,13 +15,13 @@
 #  limitations under the License.
 
 Test.Summary = '''
-Test xdebug plugin X-Remap and fwd headers
+Test xdebug plugin X-Remap, Probe and fwd headers
 '''
 
 server = Test.MakeOriginServer("server", options={'--load': (Test.TestDirectory + '/x_remap-observer.py')})
 
 request_header = {
-    "headers": "GET /argh HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
+    "headers": "GET /argh HTTP/1.1\r\nHost: doesnotmatter\r\n\r\n", "timestamp": "1469733493.993", "body": "" }
 response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n",
                    "timestamp": "1469733493.993", "body": ""}
 server.addResponse("sessionlog.json", request_header, response_header)