/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #include #include "dataimpl.h" #include "serverconn.h" namespace pvxs { namespace impl { DEFINE_LOGGER(connsetup, "pvxs.tcp.setup"); namespace { struct ServerIntrospect final : public ServerOp { ServerIntrospect(const std::shared_ptr& chan, uint32_t ioid) :ServerOp(chan, ioid) {} virtual ~ServerIntrospect() {} void doReply(const FieldDesc* type, const Status& sts) { if(state != ServerOp::Executing) return; auto ch = chan.lock(); if(!ch) return; auto conn = ch->conn.lock(); if(!conn || conn->state==ConnBase::Disconnected) return; { (void)evbuffer_drain(conn->txBody.get(), evbuffer_get_length(conn->txBody.get())); EvOutBuf R(conn->sendBE, conn->txBody.get()); to_wire(R, uint32_t(ioid)); to_wire(R, sts); if(type) to_wire(R, type); } ch->statTx += conn->enqueueTxBody(CMD_GET_FIELD); state = ServerOp::Dead; conn->opByIOID.erase(ioid); ch->opByIOID.erase(ioid); } void show(std::ostream& strm) const override final { strm<<"INFO\n"; } INST_COUNTER(ServerIntrospect); }; DEFINE_INST_COUNTER(ServerIntrospect); struct ServerIntrospectControl final : public server::ConnectOp { ServerIntrospectControl(ServerConn *conn, ServerChan *chan, const std::weak_ptr& server, const std::weak_ptr& op) :server::ConnectOp(chan->name, conn->cred, Info, Value()) // TODO: pvRequest? ,server(server) ,op(op) {} virtual ~ServerIntrospectControl() { error("Implicit Cancel"); } virtual void connect(const Value& prototype) override final { if(!prototype) throw std::logic_error("Can't reply to GET_FIELD with Null prototype"); Status sts{Status::Ok}; doReply(prototype, sts); } virtual void error(const std::string &msg) override final { Status sts{Status::Error, msg}; doReply(Value(), sts); } void doReply(const Value& prototype, const Status& sts) { auto serv = server.lock(); if(!serv) return; // soft fail if already completed, canceled, disconnected, .... auto op(this->op); serv->acceptor_loop.dispatch([op, prototype, sts](){ auto type = Value::Helper::desc(prototype); if(auto oper = op.lock()) oper->doReply(type, sts); }); } virtual void onClose(std::function&& fn) override final { auto serv = server.lock(); if(!serv) return; serv->acceptor_loop.call([this, &fn](){ if(auto oper = op.lock()) { if(oper->state != ServerOp::Dead) oper->onClose = std::move(fn); } }); } // we'll never use these, so no reason to store virtual void onGet(std::function&& fn)>&& fn) override final {} virtual void onPut(std::function&& fn, Value&&)>&& fn) override final {} virtual void logRemote(Level lvl, const std::string& msg) override final { auto serv = server.lock(); if(!serv) return; std::string m(msg); // copy for bind serv->acceptor_loop.dispatch([this, lvl, m](){ if(auto oper = op.lock()) { if(auto chan = oper->chan.lock()) { if(auto conn = chan->conn.lock()) { conn->logRemote(oper->ioid, lvl, m); } } } }); } const std::weak_ptr server; const std::weak_ptr op; INST_COUNTER(ServerIntrospectControl); }; DEFINE_INST_COUNTER(ServerIntrospectControl); } // namespace void ServerConn::handle_GET_FIELD() { // aka. GetField auto rxlen = 8u + evbuffer_get_length(segBuf.get()); EvInBuf M(peerBE, segBuf.get(), 16); uint32_t sid = -1, ioid = -1; std::string subfield; from_wire(M, sid); from_wire(M, ioid); from_wire(M, subfield); if(!M.good()) throw std::runtime_error(SB()<statRx += rxlen; auto op(std::make_shared(chan, ioid)); std::unique_ptr ctrl(new ServerIntrospectControl(this, chan.get(), iface->server->internal_self, op)); op->state = ServerOp::Executing; // this is a one-shot operation opByIOID[ioid] = op; chan->opByIOID[ioid] = op; if(chan->onOp) { try { chan->onOp(std::move(ctrl)); }catch(std::exception& e){ // a remote error will be signaled from ~ServerIntrospectControl log_err_printf(connsetup, "Client %s Info \"%s\" onOp() error: %s\n", peerName.c_str(), chan->name.c_str(), e.what()); } } } }} // namespace pvxs::impl