Repository: trafficserver
Updated Branches:
  refs/heads/master 246d3ed6c -> 442defed1


TS-2810: add TSVConnFdCreate API

Add a new TSVConnFdCreate API that binds a connected socket to a TSVConn.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/442defed
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/442defed
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/442defed

Branch: refs/heads/master
Commit: 442defed1982f67eaffe4b4e1389c23f6ab25bdd
Parents: 246d3ed
Author: James Peach <[email protected]>
Authored: Wed May 14 15:52:00 2014 -0700
Committer: James Peach <[email protected]>
Committed: Mon May 19 09:17:52 2014 -0700

----------------------------------------------------------------------
 CHANGES                                  |   2 +
 doc/reference/api/TSVConnFdCreate.en.rst |  63 ++++++++++++++++
 example/intercept/intercept.cc           | 103 ++++++++++----------------
 iocore/net/P_UnixNetVConnection.h        |   2 +-
 iocore/net/UnixNetProcessor.cc           |   2 +-
 iocore/net/UnixNetVConnection.cc         |  59 ++++++++++-----
 proxy/InkAPI.cc                          |  50 +++++++++++++
 proxy/api/ts/ts.h                        |   2 +
 8 files changed, 199 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index bb630d2..bd2d37c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.0.0
 
+  *) [TS-2810] Add the TSVConnFdCreate API.
+
   *) [TS-2342] Problem with cache.cache_responses_to_cookies value 0.
    Author: Paul Marquess <[email protected]>
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/doc/reference/api/TSVConnFdCreate.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/api/TSVConnFdCreate.en.rst 
b/doc/reference/api/TSVConnFdCreate.en.rst
new file mode 100644
index 0000000..dde0961
--- /dev/null
+++ b/doc/reference/api/TSVConnFdCreate.en.rst
@@ -0,0 +1,63 @@
+.. 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.
+
+
+.. default-domain:: c
+
+TSVConnFdCreate
+===============
+
+Create a TSVConn from a socket.
+
+Synopsis
+--------
+
+`#include <ts/ts.h>`
+
+.. c:function:: TSVConn TSVConnFdCreate(int fd)
+
+Description
+-----------
+
+:func:`TSVConnFdCreate` accepts a network socket and returns a new
+:type:`TSVConn` constructed from the socket. The socket descriptor
+must be an already connected socket. It will be placed into
+non-blocking mode.
+
+Return Values
+-------------
+
+On success, the returned TSVConn object owns the socket and the
+caller must not close it.  If :func:`TSVConnFdCreate` fails, :data:`NULL`
+is returned, the socket is unchanged and the caller must close
+it.
+
+Examples
+--------
+
+The example below is excerpted from `example/intercept/intercept.cc`
+in the Traffic Server source distribution. It demonstrates how to
+use :func:`TSVConnFdCreate` to construct a :type:`TSVConn` from a
+connected socket.
+
+.. literalinclude:: ../../../example/intercept/intercept.cc
+  :language: c
+  :lines: 288-336
+
+See also
+--------
+
+:manpage:`TSAPI(3ts)`

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/example/intercept/intercept.cc
----------------------------------------------------------------------
diff --git a/example/intercept/intercept.cc b/example/intercept/intercept.cc
index b6eda22..edd4132 100644
--- a/example/intercept/intercept.cc
+++ b/example/intercept/intercept.cc
@@ -27,6 +27,7 @@
 
 #include <ts/ts.h>
 #include <stdlib.h>
+#include <errno.h>
 #include <inttypes.h>
 #include <string.h>
 #include <netinet/in.h>
@@ -36,7 +37,7 @@
 //
 // This plugin primarily demonstrates the use of server interceptions to allow 
a
 // plugin to act as an origin server. It also demonstrates how to use
-// TSNetConnect to make a TCP connection to another server, and the how to use
+// TSVConnFdCreate to wrap a TCP connection to another server, and the how to 
use
 // the TSVConn APIs to transfer data between virtual connections.
 //
 // This plugin intercepts all cache misses and proxies them to a separate 
server
@@ -45,7 +46,6 @@
 // request. The TSQA test test-server-intercept exercises this plugin. You can
 // enable extensive logging with the "intercept" diagnostic tag.
 
-
 #define PLUGIN "intercept"
 #define PORT 60000
 
@@ -285,14 +285,11 @@ InterceptInterceptionHook(TSCont contp, TSEvent event, 
void * edata)
     // Set up the server intercept. We have the original
     // TSHttpTxn from the continuation. We need to connect to the
     // real origin and get ready to shuffle data around.
-    TSAction    action;
     char        buf[INET_ADDRSTRLEN];
     socket_type addr;
     argument_type     cdata(TSContDataGet(contp));
     InterceptState *  istate = new InterceptState();
-
-    istate->txn = cdata.txn;
-    istate->client.vc = arg.vc;
+    int fd = -1;
 
     // This event is delivered by the continuation that we
     // attached in InterceptTxnHook, so the continuation data is
@@ -306,19 +303,52 @@ InterceptInterceptionHook(TSCont contp, TSEvent event, 
void * edata)
     addr.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);  // XXX config option
     addr.sin.sin_port = htons(PORT);                    // XXX config option
 
+    // Normally, we would use TSNetConnect to connect to a secondary service, 
but to demonstrate
+    // the use of TSVConnFdCreate, we do a blocking connect inline. This it 
not recommended for
+    // production plugins, since it might block an event thread for an 
arbitrary time.
+    fd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+    TSReleaseAssert(fd != -1);
+
+    if (::connect(fd, &addr.sa, sizeof(addr.sin)) == -1) {
+      // We failed to connect to the intercepted origin. Abort the
+      // server intercept since we cannot handle it.
+      VDEBUG("connect failed with %s (%d)", strerror(errno), errno);
+      TSVConnAbort(arg.vc, TS_VC_CLOSE_ABORT);
+
+      delete istate;
+      TSContDestroy(contp);
+
+      return TS_EVENT_NONE;
+    }
+
     VDEBUG("binding client vc=%p to %s:%u",
         istate->client.vc, inet_ntop(AF_INET, &addr.sin.sin_addr, buf, 
sizeof(buf)), (unsigned)ntohs(addr.sin.sin_port));
 
+    istate->txn = cdata.txn;
+    istate->client.vc = arg.vc;
+    istate->server.vc = TSVConnFdCreate(fd);
+
     // Reset the continuation data to be our intercept state
     // block. We will need this so that we can access both of the
     // VCs at the same time. We need to do this before calling
     // TSNetConnect so that we can handle the failure case.
     TSContDataSet(contp, istate);
 
-    action = TSNetConnect(contp, &addr.sa);
-    if (TSActionDone(action)) {
-      VDEBUG("origin connection was done inline");
-    }
+    // Start reading the request from the server intercept VC.
+    istate->client.readio.read(istate->client.vc, contp);
+    VIODEBUG(istate->client.readio.vio, "started %s read", 
InterceptProxySide(istate, &istate->client));
+
+    // Start reading the response from the intercepted origin server VC.
+    istate->server.readio.read(istate->server.vc, contp);
+    VIODEBUG(istate->server.readio.vio, "started %s read", 
InterceptProxySide(istate, &istate->server));
+
+    // Start writing the response to the server intercept VC.
+    istate->client.writeio.write(istate->client.vc, contp);
+    VIODEBUG(istate->client.writeio.vio, "started %s write", 
InterceptProxySide(istate, &istate->client));
+
+    // Start writing the request to the intercepted origin server VC.
+    istate->server.writeio.write(istate->server.vc, contp);
+    VIODEBUG(istate->server.writeio.vio, "started %s write", 
InterceptProxySide(istate, &istate->server));
 
     // We should not do anything after the TSNetConnect call. The
     // NET_CONNECT events take care of everything and we don't
@@ -343,59 +373,6 @@ InterceptInterceptionHook(TSCont contp, TSEvent event, 
void * edata)
     return TS_EVENT_NONE;
   }
 
-  case TS_EVENT_NET_CONNECT: {
-    // TSNetConnect is asynchronous, so all we know right now is
-    // that the connect was scheduled. We need to kick off some
-    // I/O in order to start proxying data between the Traffic
-    // Server core and our intercepted origin server. The server
-    // intercept will begin the process by sending a read
-    // request.
-    argument_type cdata(TSContDataGet(contp));
-
-    VDEBUG("connected to intercepted origin server, binding server vc=%p to 
istate=%p", arg.vc, cdata.istate);
-
-    TSReleaseAssert(cdata.istate->client.vc != NULL);
-    cdata.istate->server.vc = arg.vc;
-
-    // Start reading the request from the server intercept VC.
-    cdata.istate->client.readio.read(cdata.istate->client.vc, contp);
-    VIODEBUG(cdata.istate->client.readio.vio, "started %s read", 
InterceptProxySide(cdata.istate, &cdata.istate->client));
-
-    // Start reading the response from the intercepted origin server VC.
-    cdata.istate->server.readio.read(cdata.istate->server.vc, contp);
-    VIODEBUG(cdata.istate->server.readio.vio, "started %s read", 
InterceptProxySide(cdata.istate, &cdata.istate->server));
-
-    // Start writing the response to the server intercept VC.
-    cdata.istate->client.writeio.write(cdata.istate->client.vc, contp);
-    VIODEBUG(cdata.istate->client.writeio.vio, "started %s write", 
InterceptProxySide(cdata.istate, &cdata.istate->client));
-
-    // Start writing the request to the intercepted origin server VC.
-    cdata.istate->server.writeio.write(cdata.istate->server.vc, contp);
-    VIODEBUG(cdata.istate->server.writeio.vio, "started %s write", 
InterceptProxySide(cdata.istate, &cdata.istate->server));
-
-    return TS_EVENT_NONE;
-  }
-
-  case TS_EVENT_NET_CONNECT_FAILED: {
-    // Connecting to the intercepted origin failed. We should
-    // pass the error on up to the server intercept.
-    argument_type cdata(TSContDataGet(contp));
-
-    VDEBUG("origin connection for txn=%p failed with error code %ld", 
cdata.istate->txn, arg.ecode);
-
-    TSReleaseAssert(cdata.istate->client.vc != NULL);
-    TSReleaseAssert(cdata.istate->server.vc == NULL);
-
-    // We failed to connect to the intercepted origin. Abort the
-    // server intercept since we cannot handle it.
-    TSVConnAbort(cdata.istate->client.vc, arg.ecode);
-
-    delete cdata.istate;
-    TSContDestroy(contp);
-
-    return TS_EVENT_NONE;
-  }
-
   case TS_EVENT_VCONN_READ_READY: {
     argument_type cdata = TSContDataGet(contp);
     TSVConn       vc    = TSVIOVConnGet(arg.vio);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/iocore/net/P_UnixNetVConnection.h
----------------------------------------------------------------------
diff --git a/iocore/net/P_UnixNetVConnection.h 
b/iocore/net/P_UnixNetVConnection.h
index 0034654..71aa3af 100644
--- a/iocore/net/P_UnixNetVConnection.h
+++ b/iocore/net/P_UnixNetVConnection.h
@@ -238,7 +238,7 @@ public:
   int startEvent(int event, Event *e);
   int acceptEvent(int event, Event *e);
   int mainEvent(int event, Event *e);
-  virtual int connectUp(EThread *t);
+  virtual int connectUp(EThread *t, int fd);
   virtual void free(EThread *t);
 
   virtual ink_hrtime get_inactivity_timeout();

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/iocore/net/UnixNetProcessor.cc
----------------------------------------------------------------------
diff --git a/iocore/net/UnixNetProcessor.cc b/iocore/net/UnixNetProcessor.cc
index 46a2f3a..92dfbcf 100644
--- a/iocore/net/UnixNetProcessor.cc
+++ b/iocore/net/UnixNetProcessor.cc
@@ -250,7 +250,7 @@ UnixNetProcessor::connect_re_internal(
       MUTEX_TRY_LOCK(lock2, get_NetHandler(t)->mutex, t);
       if (lock2) {
         int ret;
-        ret = vc->connectUp(t);
+        ret = vc->connectUp(t, NO_FD);
         if ((using_socks) && (ret == CONNECT_SUCCESS))
           return &socksEntry->action_;
         else

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/iocore/net/UnixNetVConnection.cc
----------------------------------------------------------------------
diff --git a/iocore/net/UnixNetVConnection.cc b/iocore/net/UnixNetVConnection.cc
index ea9f22a..d7f6c68 100644
--- a/iocore/net/UnixNetVConnection.cc
+++ b/iocore/net/UnixNetVConnection.cc
@@ -945,7 +945,7 @@ UnixNetVConnection::startEvent(int /* event ATS_UNUSED */, 
Event *e)
     return EVENT_CONT;
   }
   if (!action_.cancelled)
-    connectUp(e->ethread);
+    connectUp(e->ethread, NO_FD);
   else
     free(e->ethread);
   return EVENT_DONE;
@@ -1079,8 +1079,10 @@ UnixNetVConnection::mainEvent(int event, Event *e)
 
 
 int
-UnixNetVConnection::connectUp(EThread *t)
+UnixNetVConnection::connectUp(EThread *t, int fd)
 {
+  int res;
+
   thread = t;
   if (check_net_throttle(CONNECT, submit_time)) {
     check_throttle_warning();
@@ -1106,36 +1108,49 @@ UnixNetVConnection::connectUp(EThread *t)
     );
   }
 
+  // If this is getting called from the TS API, then we are wiring up a file 
descriptor
+  // provided by the caller. In that case, we know that the socket is already 
connected.
+  if (fd == NO_FD) {
+    res = con.open(options);
+    if (res != 0) {
+      goto fail;
+    }
+  } else {
+    int len = sizeof(con.sock_type);
 
-  int res = con.open(options);
-  if (0 == res) {
-    // Must connect after EventIO::Start() to avoid a race condition
-    // when edge triggering is used.
-    if (ep.start(get_PollDescriptor(t), this, EVENTIO_READ|EVENTIO_WRITE) < 0) 
{
-      lerrno = errno;
-      Debug("iocore_net", "connectUp : Failed to add to epoll list\n");
-      action_.continuation->handleEvent(NET_EVENT_OPEN_FAILED, (void *)0); // 
0 == res
-      free(t);
-      return CONNECT_FAILURE;
+    res = safe_getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&con.sock_type, 
&len);
+    if (res != 0) {
+      goto fail;
     }
-    res = con.connect(&server_addr.sa, options);
+
+    safe_nonblocking(fd);
+    con.fd = fd;
+    con.is_connected = true;
+    con.is_bound = true;
   }
 
-  if (res) {
+  // Must connect after EventIO::Start() to avoid a race condition
+  // when edge triggering is used.
+  if (ep.start(get_PollDescriptor(t), this, EVENTIO_READ|EVENTIO_WRITE) < 0) {
     lerrno = errno;
-    action_.continuation->handleEvent(NET_EVENT_OPEN_FAILED, (void 
*)(intptr_t)res);
+    Debug("iocore_net", "connectUp : Failed to add to epoll list\n");
+    action_.continuation->handleEvent(NET_EVENT_OPEN_FAILED, (void *)0); // 0 
== res
     free(t);
     return CONNECT_FAILURE;
   }
+
+  if (fd == NO_FD) {
+    res = con.connect(&server_addr.sa, options);
+    if (res != 0) {
+      goto fail;
+    }
+  }
+
   check_emergency_throttle(con);
 
   // start up next round immediately
 
   SET_HANDLER(&UnixNetVConnection::mainEvent);
-  // This function is empty for regular UnixNetVConnection, it has code
-  // in it for the inherited SSLUnixNetVConnection.  Allows the connectUp
-  // function code not to be duplicated in the inherited SSL class.
-  //  sslStartHandShake (SSL_EVENT_CLIENT, err);
 
   nh = get_NetHandler(t);
   nh->open_list.enqueue(this);
@@ -1144,6 +1159,12 @@ UnixNetVConnection::connectUp(EThread *t)
   ink_assert(!active_timeout_in);
   action_.continuation->handleEvent(NET_EVENT_OPEN, this);
   return CONNECT_SUCCESS;
+
+fail:
+  lerrno = errno;
+  action_.continuation->handleEvent(NET_EVENT_OPEN_FAILED, (void 
*)(intptr_t)res);
+  free(t);
+  return CONNECT_FAILURE;
 }
 
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/proxy/InkAPI.cc
----------------------------------------------------------------------
diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc
index 59d2203..95c0218 100644
--- a/proxy/InkAPI.cc
+++ b/proxy/InkAPI.cc
@@ -6228,6 +6228,56 @@ TSVConnCreate(TSEventFunc event_funcp, TSMutex mutexp)
   return reinterpret_cast<TSVConn>(i);
 }
 
+struct ActionSink : public Continuation
+{
+  ActionSink() : Continuation(NULL) {
+    SET_HANDLER(&ActionSink::mainEvent);
+  }
+
+  int mainEvent(int event, void * edata) {
+    // Just sink the event ...
+    Debug("iocore_net", "sinking event=%d (%s), edata=%p",
+        event, HttpDebugNames::get_event_name(event), edata);
+    return EVENT_CONT;
+  }
+};
+
+static ActionSink a;
+
+TSVConn
+TSVConnFdCreate(int fd)
+{
+  UnixNetVConnection * vc;
+
+  if (unlikely(fd == NO_FD)) {
+    return NULL;
+  }
+
+  vc = (UnixNetVConnection *)netProcessor.allocate_vc(this_ethread());
+  if (vc == NULL) {
+    return NULL;
+  }
+
+  // We need to set an Action to handle NET_EVENT_OPEN* events. Since we have a
+  // socket already, we don't need to do anything in those events, so we can 
just
+  // sink them. It's better to sink them here, than to make the NetVC code more
+  // complex.
+  vc->action_ = &a;
+
+  vc->id = net_next_connection_number();
+  vc->submit_time = ink_get_hrtime();
+  vc->set_is_transparent(false);
+  vc->mutex = new_ProxyMutex();
+
+  if (vc->connectUp(this_ethread(), fd) != CONNECT_SUCCESS) {
+    vc->free(this_ethread());
+    return NULL;
+  }
+
+  NET_SUM_GLOBAL_DYN_STAT(net_connections_currently_open_stat, 1);
+  return reinterpret_cast<TSVConn>(vc);
+}
+
 TSVIO
 TSVConnReadVIOGet(TSVConn connp)
 {

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/442defed/proxy/api/ts/ts.h
----------------------------------------------------------------------
diff --git a/proxy/api/ts/ts.h b/proxy/api/ts/ts.h
index 4e820aa..b327780 100644
--- a/proxy/api/ts/ts.h
+++ b/proxy/api/ts/ts.h
@@ -2195,6 +2195,8 @@ extern "C"
   */
   tsapi TSVConn TSVConnCreate(TSEventFunc event_funcp, TSMutex mutexp);
 
+  tsapi TSVConn TSVConnFdCreate(int fd);
+
   /* api functions to access stats */
   /* ClientResp APIs exist as well and are exposed in PrivateFrozen  */
   tsapi int TSHttpTxnClientReqHdrBytesGet(TSHttpTxn txnp);

Reply via email to