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);
