int Startup(const char *config, streamsize nbytes) { PLUGIN_INFO("startup"); g_plugin = SimplePlugin::CreatePlugin(); if(!g_plugin) { PLUGIN_INFO("creation failed"); return -1; } if(!g_plugin->OnStartup(config, nbytes)) { PLUGIN_ERROR("startup failed"); SimplePlugin::DestroyPlugin(g_plugin); g_plugin = nullptr; return -2; } g_running = true; g_running = g_thread.Initialize(&PluginLoop, nullptr); if(!g_running) { PLUGIN_ERROR("thread start failed"); } return g_running ? 0 : -1; }
int PluginLoop(void*){ WatchHandle watch = CreateWatch("*", "*", "*"); while(running){ Notification notif; if(GetNotification(¬if, 0)){ PLUGIN_INFO("notification received: %d.\n", notif.type); int res = 0; switch (notif.type) { case kShutdown: running = false; break; case kWatch: PluginInfo info; res = GetPluginInfo(notif.content.watch.plugin, &info); PLUGIN_INFO("plugin %s(%s) available\n", info.name, info.version); if(watch == notif.content.watch.handle && notif.content.watch.plugin_state == kPluginAvailable) { Connect(notif.content.watch.plugin); } break; default: break; } // process notification } } return 0; }
bool RestClientPluginTest::OnStartup(const char* config, streamsize nbytes) { bool result = RegisterCommand(&m_command); if(!result) { PLUGIN_INFO("failed to register %s", m_command.Name()); } return result; }
void OnConnected(TCPServer::Handle handle, const Base::Socket::Address &address, void *context) { PLUGIN_INFO("connection established %d. ip %s", handle, Base::Socket::Print(address)); GatePlugin *this_ = static_cast<GatePlugin *>(context); this_->AddConnection(handle, address); }
void OnDisconnected(TCPServer::Handle handle, TCPServer::CloseReason reason, const Base::Socket::Address &address, void *context) { PLUGIN_INFO("disconnected %d. reason %s. ip %s", handle, TCPServer::ToString(reason), Base::Socket::Print(address)); GatePlugin *this_ = static_cast<GatePlugin *>(context); this_->RemConnection(handle); }
bool GatePlugin::Disconnect(TCPServer::Handle id) { if(!m_users->IsConnected(id)) { PLUGIN_ERROR("not connected %d.", id); return false; } PLUGIN_INFO("closing connection id: %d", id); m_conn->Close(id, TCPServer::CloseReason::kRESTfulAPI); return true; }
ret_t cherokee_handler_zeromq_configure (cherokee_config_node_t *conf, cherokee_server_t *srv, cherokee_module_props_t **_props) { ret_t ret; cherokee_list_t *i; cherokee_handler_zeromq_props_t *props; cherokee_plugin_info_t *info = NULL; /* Instance a new property object */ if (*_props == NULL) { CHEROKEE_NEW_STRUCT (n, handler_zeromq_props); cherokee_handler_props_init_base (HANDLER_PROPS(n), MODULE_PROPS_FREE(props_free)); cherokee_buffer_init (&n->endpoint); n->io_threads = 1; CHEROKEE_MUTEX_INIT (&n->mutex, CHEROKEE_MUTEX_FAST); *_props = MODULE_PROPS(n); } props = PROP_ZEROMQ(*_props); /* Voodoo to get our own backend gzipper */ ret = cherokee_plugin_loader_get (&srv->loader, "gzip", &info); if (ret != ret_ok) { return ret; } /* Parse the configuration tree */ cherokee_config_node_foreach (i, conf) { cherokee_config_node_t *subconf = CONFIG_NODE(i); if (equal_buf_str (&subconf->key, "endpoint")) { cherokee_buffer_clean (&props->endpoint); cherokee_buffer_add_buffer (&props->endpoint, &subconf->val); } else if (equal_buf_str (&subconf->key, "io_threads")) { props->io_threads = atoi(subconf->val.buf); } else if (equal_buf_str (&subconf->key, "encoder") && info->configure) { encoder_func_configure_t configure = info->configure; props->encoder_props = NULL; ret = configure (subconf, srv, (cherokee_module_props_t **)&props->encoder_props); if (ret != ret_ok) { return ret; } props->encoder_props->instance_func = PLUGIN_INFO(info)->instance; } }
void GatePlugin::RemConnection(TCPServer::Handle handle) { redisReply *reply = static_cast<redisReply *>(redisCommand(m_redis, "DEL user:%llu", handle)); if(!reply) { PLUGIN_ERROR("redis command failed"); return; } PLUGIN_INFO("DEL: %s", reply->str); freeReplyObject(reply); }
bool GatePlugin::RedisConnectBlocking(const char *hostname, u16 port) { m_redis = redisConnect(hostname, port); if(!m_redis) { PLUGIN_ERROR("redis: can't allocate redis"); return false; } if(m_redis->err) { PLUGIN_ERROR("redis: %s", m_redis->errstr); return false; } PLUGIN_INFO("redis connected at %s:%d", hostname, port); return true; }
void GatePlugin::AddConnection(TCPServer::Handle handle, const Base::Socket::Address &address) { redisReply *reply = static_cast<redisReply *>( // redisCommand(m_redis, "SET user:%llu data", handle)); redisCommand(m_redis, "HMSET user:%llu" "id %llu" "name user_name", handle, handle)); if(!reply) { PLUGIN_ERROR("redis command failed"); return; } PLUGIN_INFO("HMSET: %s", reply->str); freeReplyObject(reply); }
void SimplePlugin::Recv(ConnectionHandle conn, void *buffer, unsigned int nbytes, std::function<void(void *, unsigned int)> func) { BASE_ASSERT(conn != 0); u32 nrecv = 0; int data_received = 0; void *work_buffer = buffer; unsigned int work_buffer_size = nbytes; int total_available = GetTotalRecvAvailable(conn); static const unsigned int kBufferTooSmall = -2; // only process limited amount of data in this call while(data_received < total_available) { do { if(nrecv == kBufferTooSmall) { // previous recv call failed because buffer was too small. allocating // bigger buffer. work_buffer_size = GetRecvAvailable(conn); work_buffer = malloc(work_buffer_size); PLUGIN_INFO("recv failed - buffer too small. %d expecting %d.", work_buffer_size, work_buffer_size); } // ((char*)work_buffer)[work_buffer_size-1] = 0; nrecv = ::Recv(conn, work_buffer, work_buffer_size /*-1*/); if(0 < nrecv) { func(work_buffer, nrecv); data_received += nrecv; } if(work_buffer != buffer) { // free temp buffer and switch back to input buffer. free(work_buffer); work_buffer = buffer; work_buffer_size = nbytes; } } while(nrecv == kBufferTooSmall); // if buffer was too small try receiving // with bigger buffer. } }
ret_t cherokee_handler_init_base (cherokee_handler_t *hdl, void *conn, cherokee_handler_props_t *props, cherokee_plugin_info_handler_t *info) { /* Init the base class */ cherokee_module_init_base (MODULE(hdl), MODULE_PROPS(props), PLUGIN_INFO(info)); /* Pure virtual methods */ hdl->read_post = NULL; hdl->add_headers = NULL; hdl->step = NULL; /* Parent reference */ hdl->connection = conn; return ret_ok; }
ret_t cherokee_validator_init_base (cherokee_validator_t *validator, cherokee_validator_props_t *props, cherokee_plugin_info_validator_t *info) { cherokee_module_init_base (MODULE(validator), MODULE_PROPS(props), PLUGIN_INFO(info)); validator->check = NULL; cherokee_buffer_init (&validator->user); cherokee_buffer_init (&validator->passwd); cherokee_buffer_init (&validator->realm); cherokee_buffer_init (&validator->response); cherokee_buffer_init (&validator->uri); cherokee_buffer_init (&validator->qop); cherokee_buffer_init (&validator->nonce); cherokee_buffer_init (&validator->cnonce); cherokee_buffer_init (&validator->algorithm); cherokee_buffer_init (&validator->nc); return ret_ok; }
bool GatePlugin::OnStartup(const char *config, streamsize nbytes) { if(!RegisterCommand(&m_list_cmd) || !RegisterCommand(&m_disconnect_cmd) || !RegisterCommand(&m_logout_cmd)) { return false; } if(!config || nbytes == 0) { PLUGIN_ERROR("no config"); return false; } tinyxml2::XMLDocument doc; tinyxml2::XMLError err = doc.Parse(config, nbytes); if(err != tinyxml2::XML_SUCCESS) { PLUGIN_ERROR("problem parsing config: %s(%d)", doc.ErrorName(), err); return false; } u16 port = 0; u32 max_connections = 0; std::string redis_hostname("localhost"); u16 redis_port = 6379; { tinyxml2::XMLElement *root = doc.RootElement(); if(root->Attribute("port")) { port = root->IntAttribute("port"); PLUGIN_INFO("port read %d", port); } else { PLUGIN_WARN("no port specified, defaulting to 0"); } if(!root->Attribute("max_connections")) { PLUGIN_ERROR("maximum connection count not specified"); return false; } max_connections = root->IntAttribute("max_connections"); PLUGIN_INFO("maximum number of connections: %d", max_connections); if(max_connections == 0) { PLUGIN_ERROR("invalid number of max connections: %d", max_connections); return false; } tinyxml2::XMLElement *redis = root->FirstChildElement("redis"); if(redis && redis->Attribute("hostname") && redis->Attribute("port")) { redis_hostname = redis->Attribute("hostname"); redis_port = redis->IntAttribute("port"); } else { PLUGIN_WARN("failed to load redis config"); } } m_users = new Users(max_connections); if(m_users == nullptr) { PLUGIN_ERROR("problem creating users data"); return false; } TCPServer::Callbacks callbacks = {Link::Gate::OnConnected, Link::Gate::OnDisconnected, Link::Gate::OnMessage}; m_conn = new TCPServer(max_connections, callbacks, this); if(!m_conn->CreateListenSocket(port)) { delete m_conn; PLUGIN_ERROR("failed to create listen port"); return false; } PLUGIN_INFO("gate listening on port: %d", port); RedisConnectBlocking(redis_hostname.c_str(), redis_port); return true; }
void OnMessage(TCPServer::Handle handle, void *buffer, u32 nbytes, void *context) { PLUGIN_INFO("data on connection %d. bytes %d", handle, nbytes); GatePlugin *this_ = static_cast<GatePlugin *>(context); this_->HandleMessage(handle, buffer, nbytes); }
void GatePlugin::HandleMessage(TCPServer::Handle handle, void *data, u32 nbytes) { // Connection& conn = m_users->GetConnection(handle); ProtobufStream stream(data, nbytes); gate::ClientMessage msg; if(!msg.ParseFromIstream(&stream)) { PLUGIN_ERROR("protocol error"); m_conn->Close(handle, TCPServer::CloseReason::kProtocolError); return; } gate::ClientMessage::Type type = msg.type(); const char *protoc = msg.protocol_version().c_str(); PLUGIN_INFO("message type: %d, protocol version: %s", type, protoc); switch(type) { case gate::ClientMessage::kLogin: { const gate::Login &login = msg.login(); gate::ServerMessage response; response.set_type(gate::ServerMessage::kLoggedIn); response.set_protocol_version(kProtocolVersion); gate::LoggedIn &logged_in = *response.mutable_logged_in(); logged_in.set_index(login.index()); index_t index = m_users->AddUser(handle, login.username().c_str()); logged_in.set_user_id(index); // todo(kstasik): validate index. send it back. if(index == kInvalidUserHandle) { PLUGIN_ERROR("problem logging in '%s'", login.username().c_str()); } PLUGIN_INFO("user '%s' logged in", login.username().c_str()); std::string serialized; response.SerializeToString(&serialized); if(!m_conn->Send(handle, static_cast<const void *>(serialized.c_str()), serialized.length())) { m_conn->Close(handle, TCPServer::CloseReason::kProtocolError); // todo(kstasik): // new error type } } break; case gate::ClientMessage::kLogout: { const gate::Logout &logout = msg.logout(); gate::ServerMessage response; response.set_type(gate::ServerMessage::kLoggedOut); response.set_protocol_version( kProtocolVersion); // todo(kstasik): refactor protoc version gate::LoggedOut &logged_out = *response.mutable_logged_out(); logged_out.set_user_id(logout.user_id()); if(!m_users->RemUser(handle, logout.user_id())) { PLUGIN_ERROR("problem logging out handle: %d index %d", handle, logout.user_id()); // todo(kstasik): handle error } else { PLUGIN_INFO("handle %d index %d logged out", handle, logout.user_id()); } std::string serialized; response.SerializeToString(&serialized); if(!m_conn->Send(handle, static_cast<const void *>(serialized.c_str()), serialized.length())) { m_conn->Close(handle, TCPServer::CloseReason::kProtocolError); // todo(kstasik): // new error type } } break; default: break; } }
int PluginLoop(void *) { PLUGIN_INFO("thread started"); g_plugin->OnThreadEntry(); unsigned int last_time = Base::Time::GetTimeMs(); while(g_running) { Notification notif; if(GetNotification(¬if, g_plugin->IdleDt())) { // PLUGIN_INFO("notification received: %d.", notif.type); g_plugin->OnNotification(notif); switch(notif.type) { case kShutdown: g_plugin->OnShutdown(notif.content.shutdown); g_running = false; break; case kWatch: { PluginInfo info; if(0 == GetPluginInfo(notif.content.watch.plugin, &info)) { PLUGIN_INFO("watch plugin match %s(%s) for %p", info.name, info.version, notif.content.watch.handle); } else { PLUGIN_WARN("unknown plugin match for %p", notif.content.connection.endpoint); } g_plugin->OnWatchMatch(notif.content.watch); break; } case kConfigChanged: g_plugin->OnConfigChange(notif.content.config); break; case kEstablished: { PluginInfo info; if(0 == GetPluginInfo(notif.content.connection.endpoint, &info)) { PLUGIN_INFO("connected to %s(%s)", info.name, info.version); } else { PLUGIN_WARN("connected to unknown plugin %p", notif.content.connection.endpoint); } g_plugin->OnConnected(notif.content.connection); break; } case kConnected: { PluginInfo info; if(0 == GetPluginInfo(notif.content.connection.endpoint, &info)) { PLUGIN_INFO("plugin connected %s(%s)", info.name, info.version); } else { PLUGIN_WARN("unknown plugin connected %p", notif.content.connection.endpoint); } g_plugin->OnPluginConnected(notif.content.connection); break; } case kDisconnected: PluginInfo info; if(0 == GetPluginInfo(notif.content.connection.endpoint, &info)) { PLUGIN_INFO("plugin disconnected %s(%s)", info.name, info.version); } else { PLUGIN_WARN("unknown plugin disconnected %p", notif.content.connection.endpoint); } g_plugin->OnDisconnected(notif.content.connection); break; case kRecvReady: g_plugin->OnRecvReady(notif.content.connection); default: break; } } unsigned int now = Base::Time::GetTimeMs(); unsigned int dt = now - last_time; g_plugin->OnUpdate(dt); last_time = now; } g_plugin->OnThreadExit(); PLUGIN_INFO("thread exiting"); return 0; }