Bé Tập Code
Bé Tập Code

Reputation: 427

How to get client IP Address with C++ in thrift

I am implementing a thrift-based (0.4.0) service in C++ at the moment and encountered a question:

Is there a way to get the client's IP address from inside a service method implementation? I am using a TNonblockingServer.

Thanks in advance!

Upvotes: 1

Views: 3303

Answers (3)

Goodroid
Goodroid

Reputation: 1

#ifndef NONBLOCK_SERVER_EVENT_HANDLER_H
#define NONBLOCK_SERVER_EVENT_HANDLER_H

#include <thrift/transport/TSocket.h>
#include <thrift/server/TServer.h>

namespace apache{
namespace thrift{
namespace server{

class ServerEventHandler:public TServerEventHandler{
        void* createContext(boost::shared_ptr<TProtocol> input, boost::shared_ptr<TProtocol> output){
    (void)input;
    (void)output;
    return (void*)(new char[32]);//TODO
  }

  virtual void deleteContext(void* serverContext, 
                                boost::shared_ptr<TProtocol>input,
                                boost::shared_ptr<TProtocol>output) {
                delete [](char*)serverContext;
  }

  virtual void processContext(void *serverContext, boost::shared_ptr<TTransport> transport){
    TSocket *tsocket = static_cast<TSocket*>(transport.get());
    if(socket){
                        struct sockaddr* addrPtr;
                        socklen_t addrLen;
      addrPtr = tsocket->getCachedAddress(&addrLen);
                  if (addrPtr){
                                        getnameinfo((sockaddr*)addrPtr,addrLen,(char*)serverContext,32,NULL,0,0) ;
                        }
    }
  }
};

}
}
}

#endif

boost::shared_ptr<ServerEventHandler> serverEventHandler(new ServerEventHandler()
server.setServerEventHandler(serverEventHandler);

Upvotes: 0

Jesse
Jesse

Reputation: 141

In TNonblockingServer, When TProcessor::process() is called the TProtocol.transport is a TMemoryBuffer, so aquiring client ip address is impossible.

But We can extend class TServerEventHandler, method TServerEventHandler::processContext() is called when a client is about to call the processor.

static boost::thread_specific_ptr<std::string> thrift_client_ip; // thread specific
class MyServerEventHandler : public TServerEventHandler
{
    virtual void processContext(void* serverContext, boost::shared_ptr<TTransport> transport)
    {
        TSocket *sock = static_cast<TSocket *>(transport.get());

        if (sock)
        {
            //thrift_client_ip.reset(new string(sock->getPeerAddress())); // 0.9.2, reused TNonblockingServer::TConnection return dirty address, see https://issues.apache.org/jira/browse/THRIFT-3270
            sock->getCachedAddress(); // use this api instead
        }
    }
};

// create nonblocking server
TNonblockingServer server(processor, protocolFactory, port, threadManager);
boost::shared_ptr<MyServerEventHandler> eventHandler(new MyServerEventHandler());
server.setServerEventHandler(eventHandler);

Upvotes: 1

JensG
JensG

Reputation: 13421

Ticket THRIFT-1053 describes a similar request for Java. The solution is basically to allow access to the inner (endpoint) transport and retrieve the data from it. Without having it really tested, building a similar solution for C++ should be easy. Since you are operating on Thrift 0.4.0, I'd strongly recommend to look at current trunk (0.9.3) first. The TBufferedTransport, TFramedTransport and TShortReadTransport already implement

boost::shared_ptr<TTransport> getUnderlyingTransport();

so the patch mentioned above may not be necessary at all.

Your TProcessor-derived class gets a hold of both transports when process() gets called. If you overwrite that method you should be able to manage access to the data you are interested in:

/**
 * A processor is a generic object that acts upon two streams of data, one
 * an input and the other an output. The definition of this object is loose,
 * though the typical case is for some sort of server that either generates
 * responses to an input stream or forwards data from one pipe onto another.
 *
 */
class TProcessor {
public:
  // more code

  virtual bool process(boost::shared_ptr<protocol::TProtocol> in,
                       boost::shared_ptr<protocol::TProtocol> out,
                       void* connectionContext) = 0;

  // more code

Upvotes: 0

Related Questions