X-Git-Url: http://developer.intra2net.com/git/?p=libt2n;a=blobdiff_plain;f=src%2Fcommand_client.cpp;h=52ca0f15774ad89403b01758bd32d2ec59756bf2;hp=77daf0dd47afd1e9e5c3339285de603e1bcd2da9;hb=238ad35f4e3b6516d4ba7611b540a0edeea71427;hpb=45a2ebc9695c4d7be6548b7e0f800d117ae56a0b diff --git a/src/command_client.cpp b/src/command_client.cpp index 77daf0d..52ca0f1 100644 --- a/src/command_client.cpp +++ b/src/command_client.cpp @@ -39,25 +39,78 @@ using namespace std; namespace libt2n { -command_client::command_client(client_connection& _c, long long _command_timeout_usec, long long _hello_timeout_usec) +command_client::command_client(client_connection* _c, long long _command_timeout_usec, long long _hello_timeout_usec) : c(_c) { command_timeout_usec=_command_timeout_usec; hello_timeout_usec=_hello_timeout_usec; // for reconnects - c.add_callback(new_connection,bind(&command_client::read_hello, boost::ref(*this))); + c->add_callback(new_connection,bind(&command_client::read_hello, boost::ref(*this))); + + // don't expect hello from an always closed connection (like dummy_client_connection) + if (!is_connection_closed()) + { + try + { + read_hello(); + } + catch (t2n_communication_error &e) + { + c->close(); + + // store a copy of the exception that you can find out details about the error later + std::auto_ptr tmp(e.clone()); + constructorException=tmp; + } + catch (...) + { + throw; + } + } +} + +/** @brief replace the connection currently in use with a new one + @param _c pointer to the new connection + + @note the old connection must still be valid when this method is called, + it can safely be deleted after this method returned + + @note all callbacks registered on the old connection will be copied over + to the new one +*/ +void command_client::replace_connection(client_connection* _c) +{ + // copy all callbacks registered on the old connection + for(callback_event_type e=static_cast(0); + e < __events_end; + e=static_cast(static_cast(e)+1)) + { + list > evcb=c->get_callback_list(e); + + for (list >::iterator i=evcb.begin(); i != evcb.end(); i++) + _c->add_callback(e,*i); + } + + // replace the connection + c=_c; read_hello(); } +/** @brief return a complete packet + @param usec_timeout maximum microseconds to wait until the packet is complete + @retval packet data as std::string + + @note throws a t2n_transfer_error if the timeout is exceeded +*/ std::string command_client::read_packet(const long long &usec_timeout) { string resultpacket; bool got_packet=false; long long my_timeout=usec_timeout; - while(!(got_packet=c.get_packet(resultpacket)) && my_timeout > 0 && !c.is_closed()) - c.fill_buffer(my_timeout,&my_timeout); + while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed()) + c->fill_buffer(my_timeout,&my_timeout); if (!got_packet) throw t2n_transfer_error("timeout exceeded"); @@ -65,40 +118,128 @@ std::string command_client::read_packet(const long long &usec_timeout) return resultpacket; } +/** @brief read and check the hello message at the beginning of a connection + + @note throws exceptions if something went wrong +*/ void command_client::read_hello() { - istringstream hello(read_packet(hello_timeout_usec)); + string resultpacket; + bool got_packet=false; + long long my_timeout=hello_timeout_usec; + while(!(got_packet=c->get_packet(resultpacket)) && my_timeout > 0 && !c->is_closed()) + { + c->fill_buffer(my_timeout,&my_timeout); - char chk[5]; - hello.read(chk,4); - chk[4]=0; - if (hello.fail() || hello.eof() || string("T2Nv") != chk) - throw t2n_version_mismatch("illegal hello received (T2N)"); + c->peek_packet(resultpacket); + check_hello(resultpacket); // will throw before timeout if wrong data received + } + + if (!got_packet) + throw t2n_transfer_error("timeout exceeded"); + + if (!check_hello(resultpacket)) + throw t2n_version_mismatch("illegal hello received (incomplete): "+resultpacket); +} + +/** @brief check if a hello message is valid + @param hellostr std::string with the message to check + @retval true if the hello is good and complete + + @note you can check incomplete hellos. you will get a false return value + but no exception. throws exceptions as soon as something is wrong. +*/ +bool command_client::check_hello(const std::string& hellostr) +{ + istringstream hello(hellostr); + + char chk; + + if (hello.read(&chk,1)) + { + if (chk != 'T') + throw t2n_version_mismatch("illegal hello received (T2N)"); + } + else + return false; + + if (hello.read(&chk,1)) + { + if (chk != '2') + throw t2n_version_mismatch("illegal hello received (T2N)"); + } + else + return false; + + if (hello.read(&chk,1)) + { + if (chk != 'N') + throw t2n_version_mismatch("illegal hello received (T2N)"); + } + else + return false; + + if (hello.read(&chk,1)) + { + if (chk != 'v') + throw t2n_version_mismatch("illegal hello received (T2N)"); + } + else + return false; int prot_version; - hello >> prot_version; - if (hello.fail() || hello.eof() || prot_version != PROTOCOL_VERSION) - throw t2n_version_mismatch("not compatible with the server protocol version"); + if (hello >> prot_version) + { + if (prot_version != PROTOCOL_VERSION) + throw t2n_version_mismatch("not compatible with the server protocol version"); + } + else + return false; + + if (hello.read(&chk,1)) + { + if (chk != ';') + throw t2n_version_mismatch("illegal hello received (1. ;)"); + } + else + return false; - hello.read(chk,1); - if (hello.fail() || hello.eof() || chk[0] != ';') - throw t2n_version_mismatch("illegal hello received (1. ;)"); + unsigned int hbo; + if (hello.read((char*)&hbo,sizeof(hbo))) + { + if (hbo != 1) + throw t2n_version_mismatch("host byte order not matching"); + } + else + return false; - hello.read(chk,4); - if (hello.fail() || hello.eof() || *((int*)chk) != 1) - throw t2n_version_mismatch("host byte order not matching"); + if (hello.read(&chk,1)) + { + if (chk != ';') + throw t2n_version_mismatch("illegal hello received (2. ;)"); + } + else + return false; - hello.read(chk,1); - if (hello.fail() || hello.eof() || chk[0] != ';') - throw t2n_version_mismatch("illegal hello received (2. ;)"); + return true; } +/** @brief send a command to the server and store the result + @param cmd pointer to a command-object + @param[out] res result container to store the result in + + @note you can check incomplete hellos. you will get a false return value + but no exception. throws exceptions as soon as something is wrong. +*/ void command_client::send_command(command* cmd, result_container &res) { ostringstream ofs; command_container cc(cmd); boost::archive::binary_oarchive oa(ofs); + if (is_connection_closed()) + throw t2n_transfer_error("connection to server is closed"); + try { oa << cc; @@ -113,14 +254,14 @@ void command_client::send_command(command* cmd, result_container &res) { throw; } std::ostream* ostr; - if ((ostr=c.get_logstream(fulldebug))!=NULL) + if ((ostr=c->get_logstream(fulldebug))!=NULL) { (*ostr) << "sending command, decoded data: " << std::endl; boost::archive::xml_oarchive xo(*ostr); xo << BOOST_SERIALIZATION_NVP(cc); } - c.write(ofs.str()); + c->write(ofs.str()); istringstream ifs(read_packet(command_timeout_usec)); boost::archive::binary_iarchive ia(ifs); @@ -138,7 +279,7 @@ void command_client::send_command(command* cmd, result_container &res) catch(...) { throw; } - if ((ostr=c.get_logstream(fulldebug))!=NULL) + if ((ostr=c->get_logstream(fulldebug))!=NULL) { (*ostr) << "received result, decoded data: " << std::endl; boost::archive::xml_oarchive xo(*ostr);