Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / net / module_l5_transport.cpp
blobe902263c798668e36d6b01c3c45a290a3095b256
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdnet.h"
18 #include "nel/net/module_gateway.h"
19 #include "nel/net/module.h"
20 #include "nel/net/module_manager.h"
21 #include "nel/net/module_socket.h"
22 #include "nel/net/module_message.h"
23 #include "nel/net/unified_network.h"
24 #include "nel/net/service.h"
25 #include "nel/net/net_log.h"
28 using namespace std;
29 using namespace NLMISC;
33 namespace NLNET
35 typedef uint8 TL5TransportId;
37 struct TTransportDesc
39 TL5TransportId TransportId;
40 string SubNetName;
41 bool InResponse;
43 void serial(NLMISC::IStream &s)
45 s.serial(TransportId);
46 s.serial(SubNetName);
47 s.serial(InResponse);
51 /** the specialized route for l5 transport */
52 class CL5Route : public CGatewayRoute
54 public:
55 /// the service ID of the outbound service
56 TServiceId ServiceId;
58 /// The transport ID at the outbound
59 TL5TransportId ForeignTransportId;
62 CL5Route(IGatewayTransport *transport)
63 : CGatewayRoute(transport)
67 void sendMessage(const CMessage &message) const;
70 /** Utility class that generate 8bits unique transport id.
71 * The total L5 transport instance is limited to 256.
72 * This really should be enough or you have a problem in
73 * your design !
74 * The allocator keep released ID as long as possible
75 * and reallocated them only when all other ids
76 * have been used/allocated.
78 class CTransportIdAllocator
80 NLMISC_SAFE_SINGLETON_DECL(CTransportIdAllocator);
81 private:
82 deque<TL5TransportId> _FreeIds;
83 set<TL5TransportId> _UsedIds;
85 CTransportIdAllocator()
87 // fill the list of free ids;
88 for (TL5TransportId i=0; i<TL5TransportId(UINT64_CONSTANT(0xffffffffffffffff)); ++i)
90 _FreeIds.push_back(i);
94 public:
95 TL5TransportId allocateId()
97 nlassert(!_FreeIds.empty());
98 TL5TransportId ret = _FreeIds.front();
99 _FreeIds.pop_front();
100 nlassert(_UsedIds.find(ret) == _UsedIds.end());
101 _UsedIds.insert(ret);
102 return ret;
105 void releaseId(TL5TransportId id)
107 nlassert(_UsedIds.find(id) != _UsedIds.end());
108 _UsedIds.erase(id);
109 _FreeIds.push_back(id);
113 NLMISC_SAFE_SINGLETON_IMPL(CTransportIdAllocator);
115 #define LAYER5_CLASS_NAME "L5Transport"
117 /** Gateway transport using layer 5 */
118 class CGatewayL5Transport : public IGatewayTransport
120 friend class CL5Route;
121 public:
122 /// This transport ID
123 TL5TransportId _TransportId;
125 /// current open status
126 bool _Open;
127 /// Subnet name
128 string _SubNetName;
130 typedef std::map<TServiceId, CL5Route*> TRouteMap;
131 /// The table that keep track of all routes
132 TRouteMap _Routes;
134 typedef std::map<TL5TransportId, CGatewayL5Transport*> TTransportDispatcher;
135 /// Global index of transport use to dispatch received message
136 static TTransportDispatcher _TransportDispatcher;
138 /// Constructor
139 CGatewayL5Transport(const IGatewayTransport::TCtorParam &param)
140 : IGatewayTransport(param),
141 _Open(false)
143 // allocate a transport unique ID
144 _TransportId = CTransportIdAllocator::getInstance().allocateId();
146 // store the transport in the dispatcher
147 _TransportDispatcher.insert(make_pair(_TransportId, this));
149 // L5 transport is always peer invisible
150 PeerInvisible = true;
153 ~CGatewayL5Transport()
155 if (_Open)
157 // the transport is still open, close it before destruction
158 close();
161 // remove the transport from the dispatcher
162 nlassert(_TransportDispatcher.find(_TransportId) != _TransportDispatcher.end());
163 _TransportDispatcher.erase(_TransportId);
164 // release the unique id
165 CTransportIdAllocator::getInstance().releaseId(_TransportId);
168 const std::string &getClassName() const
170 static string className(LAYER5_CLASS_NAME);
171 return className;
174 virtual void update()
178 virtual uint32 getRouteCount() const
180 return (uint32)_Routes.size();
183 void dump(NLMISC::CLog &log) const
185 IModuleManager &mm = IModuleManager::getInstance();
186 log.displayNL(" NeL Net layer 5 transport");
187 if (!_Open)
189 log.displayNL(" The transport is currently closed.");
191 else
193 log.displayNL(" The transport is open and support %u routes :",
194 _Routes.size());
195 TRouteMap::const_iterator first(_Routes.begin()), last(_Routes.end());
196 for (; first != last; ++first)
198 TServiceId sid = first->first;
199 CL5Route *route = first->second;
200 log.displayNL(" + route to service %hu('%s'), %u entries in the proxy translation table :",
201 sid.get(),
202 CUnifiedNetwork::getInstance()->getServiceName(sid).c_str(),
203 route->ForeignToLocalIdx.getAToBMap().size());
206 CGatewayRoute::TForeignToLocalIdx::TAToBMap::const_iterator first(route->ForeignToLocalIdx.getAToBMap().begin()), last(route->ForeignToLocalIdx.getAToBMap().end());
207 for (; first != last; ++first)
209 IModuleProxy *modProx = mm.getModuleProxy(first->second);
211 log.displayNL(" - Proxy '%s' : local proxy id %u => foreign module id %u",
212 modProx != NULL ? modProx->getModuleName().c_str() : "ERROR, invalid module",
213 first->second,
214 first->first);
221 void onCommand(const CMessage &/* command */)
223 // nothing done for now
224 throw EInvalidCommand();
226 /// The gateway send a textual command to the transport
227 bool onCommand(const TParsedCommandLine &command)
229 if (command.SubParams.size() < 1)
230 throw EInvalidCommand();
232 const std::string &commandName = command.SubParams[0]->ParamName;
233 if (commandName == "open")
235 string subNetName;
237 /// look for an optional sub network name
238 const TParsedCommandLine *netName = command.SubParams[0]->getParam("SubNet");
239 if (netName != NULL)
241 subNetName = netName->ParamValue;
244 open(subNetName);
246 else if (commandName == "close")
248 close();
250 else
251 return false;
253 return true;
257 /// Open the server by establishing route with all known services
258 void open(const std::string &subNetName)
260 H_AUTO(L5_open);
262 static TUnifiedCallbackItem L5TransportCallback[] =
264 {"GW_L5_MSG", CGatewayL5Transport::cbDispatchL5Message },
265 {"GW_L5_ADDTP", CGatewayL5Transport::cbL5AddTransport },
266 {"GW_L5_REMTP", CGatewayL5Transport::cbL5RemoveTransport },
269 if (_Open == true)
270 throw ETransportError("Transport already open");
272 _SubNetName = subNetName;
275 CUnifiedNetwork *un = CUnifiedNetwork::getInstance();
277 static bool callbackRegistered = false;
279 if (!callbackRegistered)
281 LNETL6_DEBUG("LNETL6: L5 transport open : registering callbacks");
282 // set the service con/disconnect callback
283 un->setServiceUpCallback("*", CGatewayL5Transport::cbOnServiceUp);
284 un->setServiceDownCallback("*", CGatewayL5Transport::cbOnServiceDown);
285 // set the message callback
286 un->addCallbackArray(L5TransportCallback, sizeof(L5TransportCallback) / sizeof(TUnifiedCallbackItem));
288 callbackRegistered = true;
292 // create route and open route for each existing service
293 const vector<TServiceId> &connList = un->getConnectionList();
295 set<TServiceId> uniqueService(connList.begin(), connList.end());
297 while (!uniqueService.empty())
299 TServiceId sid = *(uniqueService.begin());
300 uniqueService.erase(uniqueService.begin());
302 if ( un->isConnectionConnected(sid))
304 // send transport descriptor to other service
305 onServiceUp(un->getServiceName(sid), sid);
307 else
309 // the Connection is not established right now. We wait for the ServiceUp callback
314 _Open = true;
317 /// Close the server, this will close all route
318 void close()
320 H_AUTO(L5_close);
322 if (_Open == false)
323 throw ETransportError("closeServer : The server is not open");
325 // close all client connections
326 while (!_Routes.empty())
328 CL5Route *route = _Routes.begin()->second;
329 TServiceId sid = route->ServiceId;
330 onServiceDown(CUnifiedNetwork::getInstance()->getServiceName(sid), sid);
333 _Open = false;
337 /***************************************************/
338 /** Event management **/
339 /***************************************************/
341 void onServiceUp(const std::string &serviceName, TServiceId sid)
343 H_AUTO(L5_onServiceUp);
345 LNETL6_DEBUG("LNETL6: L5 transport onServiceUp('%s')", serviceName.c_str());
346 // send the transport descriptor to the new service
347 TTransportDesc desc;
348 desc.SubNetName = _SubNetName;
349 desc.TransportId = _TransportId;
350 desc.InResponse = false;
352 CMessage msg("GW_L5_ADDTP");
353 msg.serial(desc);
355 CUnifiedNetwork::getInstance()->send(sid, msg);
357 // the route will be created by receiving this message
360 void onServiceDown(const std::string &/* serviceName */, TServiceId sid)
362 H_AUTO(L5_onServicedown);
364 LNETL6_DEBUG("LNETL6: L5 transport onServiceDown('%hu')", sid.get());
365 // retrieve the route
366 TRouteMap::iterator it(_Routes.find(sid));
367 if (it == _Routes.end())
369 nlinfo("Transport L5 : service down, can't find a route for the service");
370 return;
372 CL5Route *route = it->second;
374 // warn the gateway
375 _Gateway->onRouteRemoved(route);
377 // release the route
378 _Routes.erase(it);
379 delete route;
382 // Called to dispatch an incoming message to the gateway
383 void onDispatchMessage(const CMessage &msgin, TServiceId sid)
385 H_AUTO(L5_onDispatchMessage);
387 LNETL6_DEBUG("LNETL6: L5 transport onDispatchMessage from service %hu", sid.get());
388 /// retrieve the route for dispatching
389 TRouteMap::iterator it(_Routes.find(sid));
390 if (it == _Routes.end())
392 nlwarning("Gateway '%s' : Can't find route for service %hu for dispatching, message is discarded",
393 _Gateway->getGatewayName().c_str(),
394 sid.get());
395 return;
398 // read the message size
399 uint32 msgLen;
400 nlRead(msgin, serial, msgLen);
402 // lock the sub message
403 msgin.lockSubMessage(msgLen);
405 _Gateway->onReceiveMessage(it->second, msgin);
407 // unlock the sub message
408 msgin.unlockSubMessage();
411 void onAddTransport(TServiceId sid, TTransportDesc &desc)
413 H_AUTO(L5_onAddTransport);
415 LNETL6_DEBUG("LNETL6: L5 transport onAddTransport from service %hu", sid.get());
416 // we need to create a route for this transport
417 // create a new route and send the route open message
419 if (_Routes.find(sid) != _Routes.end())
421 LNETL6_DEBUG("LNETL6: L5 transport onAddTransport a route for this service alredy exist");
422 return;
425 CL5Route *route = new CL5Route(this);
427 route->ServiceId = sid;
428 route->ForeignTransportId = desc.TransportId;
430 // store the route infos
431 _Routes.insert(make_pair(sid, route));
433 // notify the gateway about the new route
434 _Gateway->onRouteAdded(route);
436 if (desc.InResponse == false)
438 // we need to send back this transport info to this service
439 TTransportDesc desc;
440 desc.InResponse = true;
441 desc.SubNetName = _SubNetName;
442 desc.TransportId = _TransportId;
444 CMessage msg("GW_L5_ADDTP");
445 msg.serial(desc);
447 CUnifiedNetwork::getInstance()->send(sid, msg);
451 void onRemoveTransport(TServiceId sid, TTransportDesc &desc)
453 H_AUTO(L5_onRemoveTransport);
455 LNETL6_DEBUG("LNETL6: L5 transport onRemoveTransport from service %hu", sid.get());
456 // Remove the route
457 TRouteMap::iterator it(_Routes.find(sid));
458 if (it == _Routes.end())
460 nlwarning("onRemoveTransport : can't find a route to the transport %hu on service %u",
461 desc.TransportId,
462 sid.get());
463 return;
466 CL5Route *route = it->second;
468 // notify the gateway about the removed route
469 _Gateway->onRouteRemoved(route);
471 // erase the route info and delete the route
472 _Routes.erase(it);
473 delete route;
476 /***************************************************/
477 /** static callback forwarder **/
478 /***************************************************/
479 /// callback from layer 5
481 static void cbL5AddTransport(CMessage &msgin, const std::string &/* serviceName */, TServiceId sid)
483 LNETL6_DEBUG("LNETL6: L5 transport cbL5AddTransport from service %hu", sid.get());
484 // Receive a transport descriptor from another service, create
485 // a route for it
487 TTransportDesc desc;
489 msgin.serial(desc);
491 // for each existing transport here, check if they are in the
492 // same sub net, if so, callback them for route creation
494 TTransportDispatcher::iterator first(_TransportDispatcher.begin()), last(_TransportDispatcher.end());
495 for (; first != last; ++first)
497 CGatewayL5Transport *transport = first->second;
498 if (transport->_Open
499 && transport->_SubNetName == desc.SubNetName
500 && (sid != IService::getInstance()->getServiceId()
501 || desc.TransportId != transport->_TransportId))
503 // this one is on the same subnet
504 transport->onAddTransport(sid, desc);
509 static void cbL5RemoveTransport(CMessage &msgin, const std::string &/* serviceName */, TServiceId sid)
511 LNETL6_DEBUG("LNETL6: L5 transport cbL5RemoveTransport from service %hu", sid.get());
512 // Receive a transport descriptor from another service, delete
513 // the route for it
515 TTransportDesc desc;
517 msgin.serial(desc);
519 // for each existing transport here, check if they are in the
520 // same sub net, if so, callback them for route creation
522 TTransportDispatcher::iterator first(_TransportDispatcher.begin()), last(_TransportDispatcher.end());
523 for (; first != last; ++first)
525 CGatewayL5Transport *transport = first->second;
526 if (transport->_Open
527 && transport->_SubNetName == desc.SubNetName
528 && desc.TransportId != transport->_TransportId)
530 // this one is on the same subnet
531 transport->onRemoveTransport(sid, desc);
536 static void cbDispatchL5Message (CMessage &msgin, const std::string &serviceName, TServiceId sid)
538 LNETL6_DEBUG("LNETL6: L5 transport cbDispatch called, receiving from %s", serviceName.c_str());
539 // dispatch the message to the route associated with the service
540 // the first info in the message is the transport id
541 TL5TransportId transportId;
542 msgin.serial(transportId);
544 // look for a corresponding transport
545 TTransportDispatcher::iterator it(_TransportDispatcher.find(transportId));
546 if (it == _TransportDispatcher.end())
548 nlwarning("ReceiveL5Message, can't find transport id %u for dispatching, message is discarded",
549 transportId);
550 return;
553 CGatewayL5Transport *transport = it->second;
554 transport->onDispatchMessage(msgin, sid);
557 static void cbOnServiceUp (const std::string &serviceName, TServiceId sid, void * /* arg */)
559 LNETL6_DEBUG("LNETL6: L5 transport cbOnServiceUp called, service up for %s", serviceName.c_str());
560 // callback all open transport about the new service
561 TTransportDispatcher::iterator first(_TransportDispatcher.begin()), last(_TransportDispatcher.end());
562 for (; first != last; ++first)
564 CGatewayL5Transport *transport = first->second;
566 if (transport->_Open)
567 transport->onServiceUp(serviceName, sid);
571 static void cbOnServiceDown (const std::string &serviceName, TServiceId sid, void * /* arg */)
573 LNETL6_DEBUG("LNETL6: L5 transport cbOnServicedown called, service down for %s", serviceName.c_str());
574 // callback all open transport about the removed service
575 TTransportDispatcher::iterator first(_TransportDispatcher.begin()), last(_TransportDispatcher.end());
576 for (; first != last; ++first)
578 CGatewayL5Transport *transport = first->second;
580 if (transport->_Open)
581 transport->onServiceDown(serviceName, sid);
587 CGatewayL5Transport::TTransportDispatcher CGatewayL5Transport::_TransportDispatcher;
589 // register this class in the transport factory
590 NLMISC_REGISTER_OBJECT(IGatewayTransport, CGatewayL5Transport, std::string, string(LAYER5_CLASS_NAME));
592 void CL5Route::sendMessage(const CMessage &message) const
594 NLNET_AUTO_DELTE_ASSERT;
595 H_AUTO(L5Route_sendMessage);
597 CGatewayL5Transport *trpt = static_cast<CGatewayL5Transport*>(_Transport);
599 // create a transport message
600 CMessage wrapper("GW_L5_MSG");
601 // serial the transport identifier
602 wrapper.serial(trpt->_TransportId);;
604 // insert the message in the wrapper
605 nlWrite(wrapper, serialMessage, message);
606 // send the message
607 CUnifiedNetwork::getInstance()->send(ServiceId, wrapper);
612 void forceGatewayL5TransportLink()
616 } // namespace NLNET