| /* |
| * 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 <thrift/server/TClientInfo.h> |
| |
| namespace apache { namespace thrift { namespace server { |
| |
| using namespace apache::thrift; |
| using namespace apache::thrift::transport; |
| |
| TClientInfoConnection::TClientInfoConnection() { |
| call_[kNameLen - 1] = '\0'; // insure NUL terminator is there |
| eraseAddr(); |
| eraseCall(); |
| } |
| |
| void TClientInfoConnection::recordAddr(const sockaddr* addr) { |
| eraseAddr(); |
| initTime(); |
| ncalls_ = 0; |
| if (addr != NULL) { |
| if (addr->sa_family == AF_INET) { |
| memcpy((void*)&addr_.ipv4, (const void *)addr, sizeof(sockaddr_in)); |
| } |
| else if (addr->sa_family == AF_INET6) { |
| memcpy((void*)&addr_.ipv6, (const void *)addr, sizeof(sockaddr_in6)); |
| } |
| } |
| } |
| |
| void TClientInfoConnection::eraseAddr() { |
| addr_.ipv4.sin_family = AF_UNSPEC; |
| } |
| |
| const char* TClientInfoConnection::getAddr(char* buf, int len) const { |
| switch (addr_.ipv4.sin_family) { |
| case AF_INET: |
| return inet_ntop(AF_INET, &addr_.ipv4.sin_addr, buf, len); |
| case AF_INET6: |
| return inet_ntop(AF_INET6, &addr_.ipv6.sin6_addr, buf, len); |
| default: |
| return NULL; |
| } |
| } |
| |
| void TClientInfoConnection::recordCall(const char* name) { |
| strncpy(call_, name, kNameLen - 1); // NUL terminator set in constructor |
| ncalls_++; |
| } |
| |
| void TClientInfoConnection::eraseCall() { |
| call_[0] = '\0'; |
| } |
| |
| const char* TClientInfoConnection::getCall() const { |
| if (call_[0] == '\0') { |
| return NULL; |
| } |
| return call_; |
| } |
| |
| void TClientInfoConnection::getTime(timespec* time) const { |
| *time = time_; |
| } |
| |
| uint64_t TClientInfoConnection::getNCalls() const { |
| return ncalls_; |
| } |
| |
| void TClientInfoConnection::initTime() { |
| clock_gettime(CLOCK_REALTIME, &time_); |
| } |
| |
| |
| TClientInfoConnection* TClientInfo::getConnection(int fd, bool grow) { |
| if (fd < 0 || (!grow && fd >= info_.size())) { |
| return NULL; |
| } |
| return &info_[fd]; |
| } |
| |
| size_t TClientInfo::size() const { |
| return info_.size(); |
| } |
| |
| void* TClientInfoServerHandler::createContext(boost::shared_ptr<TProtocol> input, |
| boost::shared_ptr<TProtocol> output) { |
| (void)input; |
| (void)output; |
| return (void*) new Connect(&clientInfo_); |
| } |
| |
| void TClientInfoServerHandler::deleteContext(void* connectionContext, |
| boost::shared_ptr<TProtocol> input, |
| boost::shared_ptr<TProtocol> output) { |
| Connect* call = static_cast<Connect*>(connectionContext); |
| if (call->callInfo_) { |
| call->callInfo_->eraseCall(); |
| } |
| delete call; |
| } |
| |
| void TClientInfoServerHandler::processContext(void* connectionContext, |
| shared_ptr<TTransport> transport) { |
| Connect* call = static_cast<Connect*>(connectionContext); |
| if (call->callInfo_ == NULL) { |
| if (typeid(*(transport.get())) == typeid(TSocket)) { |
| TSocket* tsocket = static_cast<TSocket*>(transport.get()); |
| int fd = tsocket->getSocketFD(); |
| if (fd < 0) { |
| return; |
| } |
| call->callInfo_ = call->clientInfo_->getConnection(fd, true); |
| assert(call->callInfo_ != NULL); |
| socklen_t len; |
| call->callInfo_->recordAddr(tsocket->getCachedAddress(&len)); |
| } |
| } |
| } |
| |
| void TClientInfoServerHandler::getStatsStrings(vector<string>& result) { |
| result.clear(); |
| timespec now; |
| clock_gettime(CLOCK_REALTIME, &now); |
| |
| for (int i = 0; i < clientInfo_.size(); ++i) { |
| TClientInfoConnection* info = clientInfo_.getConnection(i, false); |
| const char* callStr = info->getCall(); |
| if (callStr == NULL) { |
| continue; |
| } |
| |
| char addrBuf[INET6_ADDRSTRLEN]; |
| const char* addrStr = info->getAddr(addrBuf, sizeof addrBuf); |
| if (addrStr == NULL) { |
| // cerr << "no addr!" << endl; |
| continue; |
| } |
| |
| timespec start; |
| double secs = 0.0; |
| info->getTime(&start); |
| secs = (double)(now.tv_sec - start.tv_sec) + (now.tv_nsec - start.tv_nsec)*0.000000001; |
| |
| char buf[256]; |
| snprintf(buf, sizeof buf, "%d %s %s %.3f %llu", i, addrStr, callStr, secs, |
| (uint64_t)info->getNCalls()); |
| |
| result.push_back(buf); |
| } |
| } |
| |
| void* TClientInfoCallHandler::getContext(const char* fn_name, void* serverContext) { |
| if (serverContext) { |
| TClientInfoConnection* callInfo = static_cast<TClientInfoServerHandler::Connect*>(serverContext)->callInfo_; |
| if (callInfo != NULL) { |
| callInfo->recordCall(fn_name); |
| } |
| } |
| return NULL; |
| } |
| |
| } } } // namespace apache::thrift::server |