Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | Related Pages

connectionbosh.cpp

00001 /*
00002  * Copyright (c) 2007-2008 by Jakob Schroeter <js@camaya.net>
00003  * This file is part of the gloox library. http://camaya.net/gloox
00004  *
00005  * This software is distributed under a license. The full license
00006  * agreement can be found in the file LICENSE in this distribution.
00007  * This software may not be copied, modified, sold or distributed
00008  * other than expressed in the named license agreement.
00009  *
00010  * This software is distributed without any warranty.
00011  */
00012 
00013 #include "gloox.h"
00014 
00015 #include "connectionbosh.h"
00016 #include "logsink.h"
00017 #include "prep.h"
00018 #include "tag.h"
00019 #include "util.h"
00020 
00021 #include <string>
00022 #include <sstream>
00023 
00024 #include <cstdlib>
00025 #include <ctime>
00026 #include <cctype>
00027 #include <algorithm>
00028 
00029 namespace gloox
00030 {
00031 
00032   ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance,
00033                                   const std::string& boshHost, const std::string& xmppServer,
00034                                   int xmppPort )
00035     : ConnectionBase( 0 ),
00036       m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
00037       m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ),
00038       m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
00039       m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
00040       m_connMode( ModePipelining )
00041   {
00042     initInstance( connection, xmppServer, xmppPort );
00043   }
00044 
00045   ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection,
00046                                   const LogSink& logInstance, const std::string& boshHost,
00047                                   const std::string& xmppServer, int xmppPort )
00048     : ConnectionBase( cdh ),
00049       m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ),
00050       m_rid( 0 ),  m_initialStreamSent( false ), m_openRequests( 0 ),
00051       m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 2 ), m_streamRestart( false ),
00052       m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ),
00053       m_connMode( ModePipelining )
00054   {
00055     initInstance( connection, xmppServer, xmppPort );
00056   }
00057 
00058   void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer,
00059                                      const int xmppPort )
00060   {
00061 // FIXME: check return value
00062     prep::idna( xmppServer, m_server );
00063     m_port = xmppPort;
00064     if( m_port != -1 )
00065     {
00066       std::ostringstream strBOSHHost;
00067       strBOSHHost << m_boshHost << ":" << m_port;
00068       m_boshedHost = strBOSHHost.str();
00069     }
00070 
00071     // drop this connection into our pool of available connections
00072     printf( "Added initial connection to connection pool\n" );
00073     printf( "Connections in pool: %d\n", m_connectionPool.size() );
00074     if( connection )
00075     {
00076       connection->registerConnectionDataHandler( this );
00077       m_connectionPool.push_back( connection );
00078     }
00079     printf( "Connections in pool: %d\n", m_connectionPool.size() );
00080   }
00081 
00082   ConnectionBOSH::~ConnectionBOSH()
00083   {
00084 //     util::clearList( m_activeConnections );
00085     // FIXME
00086     {
00087       ConnectionList::iterator it = m_activeConnections.begin();
00088       ConnectionList::iterator it2;
00089       while( it != m_activeConnections.end() )
00090       {
00091         it2 = it++;
00092         delete (*it2);
00093         m_activeConnections.erase( it2 );
00094       }
00095     }
00096     // ~
00097 
00098 //     util::clearList( m_connectionPool );
00099     // FIXME
00100     {
00101       ConnectionList::iterator it = m_connectionPool.begin();
00102       ConnectionList::iterator it2;
00103       while( it != m_connectionPool.end() )
00104       {
00105         it2 = it++;
00106         delete (*it2);
00107         m_connectionPool.erase( it2 );
00108       }
00109     }
00110     // ~
00111   }
00112 
00113   ConnectionBase* ConnectionBOSH::newInstance() const
00114   {
00115     ConnectionBase* pBaseConn = 0;
00116 
00117     if( !m_connectionPool.empty() )
00118     {
00119       pBaseConn = m_connectionPool.front()->newInstance();
00120     }
00121     else if( !m_activeConnections.empty() )
00122     {
00123       pBaseConn = m_activeConnections.front()->newInstance();
00124     }
00125     else
00126     {
00127       return 0;
00128     }
00129 
00130     return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance,
00131                                m_boshHost, m_server, m_port );
00132   }
00133 
00134   ConnectionError ConnectionBOSH::connect()
00135   {
00136     if( m_state >= StateConnecting )
00137       return ConnNoError;
00138 
00139     if( !m_handler )
00140       return ConnNotConnected;
00141 
00142     m_state = StateConnecting;
00143     m_logInstance.dbg( LogAreaClassConnectionBOSH,
00144                        "bosh initiating connection to server: " +
00145                        ( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" )
00146                          : ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" )
00147                                                               : std::string( "PersistentHTTP" ) ) ) );
00148     getConnection();
00149     printf( "XXXXXXXXXXXXXXX connect() about to return\n" );
00150     return ConnNoError; // FIXME?
00151   }
00152 
00153   void ConnectionBOSH::disconnect()
00154   {
00155     if( ( m_connMode == ModePipelining && m_activeConnections.empty() )
00156         || ( m_connectionPool.empty() && m_activeConnections.empty() ) )
00157       return;
00158 
00159     if( m_state != StateDisconnected )
00160     {
00161       ++m_rid;
00162 
00163       std::ostringstream requestBody;
00164 
00165       requestBody << "<body ";
00166       requestBody << "rid='" << m_rid << "' ";
00167       requestBody << "sid='" << m_sid << "' ";
00168       requestBody << "type='terminal' ";
00169       requestBody << "xml:lang='en' ";
00170       requestBody << "xmlns='" << XMLNS_HTTPBIND << "'";
00171       if( m_sendBuffer.empty() )  // Make sure that any data in the send buffer gets sent
00172         requestBody << "/>";
00173       else
00174       {
00175         requestBody << ">" << m_sendBuffer << "</body>";
00176         m_sendBuffer = EmptyString;
00177       }
00178       sendRequest( requestBody.str() );
00179 
00180       m_logInstance.dbg( LogAreaClassConnectionBOSH, "bosh disconnection request sent" );
00181     }
00182     else
00183     {
00184       m_logInstance.err( LogAreaClassConnectionBOSH,
00185                          "disconnecting from server in a non-graceful fashion" );
00186     }
00187 
00188     util::ForEach( m_activeConnections, &ConnectionBase::disconnect );
00189     util::ForEach( m_connectionPool, &ConnectionBase::disconnect );
00190 
00191     m_state = StateDisconnected;
00192     if( m_handler )
00193       m_handler->handleDisconnect( this, ConnUserDisconnected );
00194   }
00195 
00196   ConnectionError ConnectionBOSH::recv( int timeout )
00197   {
00198     if( m_state == StateDisconnected )
00199       return ConnNotConnected;
00200 
00201     printf( "recv, before\n" );
00202     printf( "active connections:   %d\n", m_activeConnections.size() );
00203     printf( "inactive connections: %d\n", m_connectionPool.size() );
00204     if( !m_connectionPool.empty() )
00205       m_connectionPool.front()->recv( 0 );
00206     if( !m_activeConnections.empty() )
00207       m_activeConnections.front()->recv( timeout );
00208 
00209     // If there are no open requests then the spec allows us to send an empty request...
00210     // (Some CMs do not obey this, it seems)
00211     if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected )
00212     {
00213       m_logInstance.dbg( LogAreaClassConnectionBOSH,
00214                          "Sending empty request (or there is data in the send buffer)" );
00215       sendXML();
00216     }
00217 
00218     return ConnNoError; // FIXME?
00219   }
00220 
00221   bool ConnectionBOSH::send( const std::string& data )
00222   {
00223 
00224     if( m_state == StateDisconnected )
00225       return false;
00226 
00227     printf( "\nTold to send: %s\n", data.c_str() );
00228 
00229     if( data.substr( 0, 2 ) == "<?" )
00230     {
00231       if( m_initialStreamSent )
00232       {
00233         m_streamRestart = true;
00234         sendXML();
00235         return true;
00236       }
00237       else
00238       {
00239         m_initialStreamSent = true;
00240         m_logInstance.dbg( LogAreaClassConnectionBOSH, "initial <stream:stream> dropped" );
00241         return true;
00242       }
00243     }
00244     else if( data == "</stream:stream>" )
00245       return true;
00246 
00247     m_sendBuffer += data;
00248     sendXML();
00249 
00250     return true;
00251   }
00252 
00253   /* Sends XML. Wraps data in a <body/> tag, and then passes to sendRequest(). */
00254   bool ConnectionBOSH::sendXML()
00255   {
00256     printf( "ConnectionBOSH::sendXML()\n" );
00257 
00258     if( m_state != StateConnected )
00259     {
00260       m_logInstance.warn( LogAreaClassConnectionBOSH,
00261                          "Data sent before connection established (will be buffered)" );
00262       printf( "~ConnectionBOSH::sendXML() not connected\n" );
00263       return false;
00264     }
00265 
00266     if( m_sendBuffer.empty() )
00267     {
00268       int now = time( 0 );
00269       unsigned int delta = now - m_lastRequestTime;
00270       if( delta < m_minTimePerRequest && m_openRequests > 0 )
00271       {
00272         m_logInstance.dbg( LogAreaClassConnectionBOSH, "too little time between requests" );
00273         printf( "~ConnectionBOSH::sendXML() send buffer empty\n" );
00274         return false;
00275       }
00276       printf( "\n>>>>> %ld seconds since last empty request (m_minTimePerRequest: %d) <<<<<\n",
00277               delta, m_minTimePerRequest );
00278       m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending empty request" );
00279     }
00280 
00281     ++m_rid;
00282 
00283     std::ostringstream requestBody;
00284     requestBody << "<body ";
00285     requestBody << "rid='" << m_rid << "' ";
00286     requestBody << "sid='" << m_sid << "' ";
00287     requestBody << "xmlns='" << XMLNS_HTTPBIND << "'";
00288 
00289     if( m_streamRestart )
00290     {
00291       requestBody << " xmpp:restart='true' to='" << m_server << "' xml:lang='en' xmlns:xmpp='"
00292           << XMLNS_XMPP_BOSH << "' />";
00293       m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" );
00294     }
00295     else
00296     {
00297       requestBody << ">" << m_sendBuffer << "</body>";
00298     }
00299     // Send a request. Force if we are not sending an empty request, or if there are no connections open
00300     if( sendRequest( requestBody.str() ) )
00301     {
00302       m_logInstance.dbg( LogAreaClassConnectionBOSH, "successfully sent m_sendBuffer" );
00303       m_sendBuffer = EmptyString;
00304     }
00305     else
00306     {
00307       m_rid--; // I think... (may need to rethink when acks are implemented)
00308       m_logInstance.warn( LogAreaClassConnectionBOSH,
00309                          "Unable to send. Connection not complete, or too many open requests,"
00310                          " so added to buffer.\n" );
00311       printf( "---------------Buffer---------------\n%s\n----------------EOB--------------\n",
00312               m_sendBuffer.c_str() );
00313     }
00314 
00315     printf( "~ConnectionBOSH::sendXML()\n" );
00316     return true;
00317   }
00318 
00319   /* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */
00320   bool ConnectionBOSH::sendRequest( const std::string& xml )
00321   {
00322     printf( "ConnectionBOSH::sendRequest()\n" );
00323     ConnectionBase* conn = getConnection();
00324     if( !conn )
00325     {
00326       printf( "ConnectionBOSH::sendRequest() no conn\n" );
00327       return false;
00328     }
00329 
00330     std::ostringstream request;
00331     request << "POST " << m_path;
00332 
00333     if( m_connMode == ModeLegacyHTTP )
00334     {
00335       request << " HTTP/1.0\r\n";
00336       request << "Connection: close\r\n";
00337     }
00338     else
00339       request << " HTTP/1.1\r\n";
00340 
00341     request << "Host: " << m_boshedHost << "\r\n";
00342     request << "Content-Type: text/xml; charset=utf-8\r\n";
00343     request << "Content-Length: " << xml.length() << "\r\n";
00344     request << "User-Agent: " << "gloox/" << GLOOX_VERSION << "\r\n\r\n";
00345     request << xml;
00346 
00347 
00348     if( conn->send( request.str() ) )
00349     {
00350       m_lastRequestTime = time( 0 );
00351       printf( "Request sent on %p:\n%s\n", conn, request.str().c_str() );
00352       ++m_openRequests;
00353       printf( "%d: Incrementing m_openRequests to %d (connections: %d)\n", (int)time( 0 ),
00354               m_openRequests, m_activeConnections.size() );
00355       printf( "~ConnectionBOSH::sendRequest() send suceeded\n" );
00356       return true;
00357     }
00358     else // FIXME What to do in this case?
00359       printf( "Error while trying to send on socket (state: %d)\n", conn->state() );
00360 
00361     printf( "~ConnectionBOSH::sendRequest()\n" );
00362     return false;
00363   }
00364 
00365   bool ci_equal( char ch1, char ch2 )
00366   {
00367     return std::toupper( (unsigned char)ch1 ) == std::toupper( (unsigned char)ch2 );
00368   }
00369 
00370   std::string::size_type ci_find( const std::string& str1, const std::string& str2 )
00371   {
00372     std::string::const_iterator pos = std::search( str1.begin(), str1.end(),
00373                                                    str2.begin(), str2.end(), ci_equal );
00374     if( pos == str1.end() )
00375       return std::string::npos;
00376     else
00377       return std::distance( str1.begin(), pos );
00378   }
00379 
00380   const std::string ConnectionBOSH::getHTTPField( const std::string& field )
00381   {
00382     const std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " )
00383                                         + field.length() + 4;
00384     if( fp == std::string::npos )
00385       return EmptyString;
00386 
00387     const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp );
00388     if( fp2 == std::string::npos )
00389       return EmptyString;
00390 
00391     return m_bufferHeader.substr( fp, fp2 - fp );
00392   }
00393 
00394   ConnectionError ConnectionBOSH::receive()
00395   {
00396     printf( "put in receive mode\n" );
00397     ConnectionError err = ConnNoError;
00398     while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError )
00399       ;
00400     printf( "err: %d, m_state: %d\n", err, m_state );
00401     return err == ConnNoError ? ConnNotConnected : err;
00402   }
00403 
00404   void ConnectionBOSH::cleanup()
00405   {
00406     m_state = StateDisconnected;
00407 
00408     util::ForEach( m_activeConnections, &ConnectionBase::cleanup );
00409     util::ForEach( m_connectionPool, &ConnectionBase::cleanup );
00410   }
00411 
00412   void ConnectionBOSH::getStatistics( int& totalIn, int& totalOut )
00413   {
00414     util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut );
00415     util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut );
00416   }
00417 
00418   void ConnectionBOSH::handleReceivedData( const ConnectionBase* connection,
00419                                            const std::string& data )
00420   {
00421     printf( "received: -------------------\n%s\n-----------------------\n", data.c_str() );
00422     m_buffer += data;
00423     if( m_buffer != data  )
00424       printf( "buffer: -------------------\n%s\n-----------------------\n", m_buffer.c_str() );
00425 
00426     std::string::size_type headerLength = 0;
00427     while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos )
00428     {
00429       m_bufferHeader = m_buffer.substr( 0, headerLength );
00430 
00431       const std::string& statusCode = m_bufferHeader.substr( 9, 3 );
00432       printf( "status code: %s\n", statusCode.c_str() );
00433       if( statusCode != "200" )
00434       {
00435         m_logInstance.warn( LogAreaClassConnectionBOSH,
00436                             "Received error via legacy HTTP status code: " + statusCode
00437                                 + ". Disconnecting." );
00438         m_state = StateDisconnected; // As per XEP, consider connection broken
00439         disconnect();
00440       }
00441 
00442       m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() );
00443       if( !m_bufferContentLength )
00444         return;
00445 
00446       if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close"
00447                                             || m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) )
00448       {
00449         m_logInstance.dbg( LogAreaClassConnectionBOSH,
00450                             "Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" );
00451         m_connMode = ModeLegacyHTTP;
00452       }
00453 
00454       printf( "buffer: %d\nheader: %d\ncontent: %d\n", m_buffer.length(), headerLength,
00455               m_bufferContentLength );
00456 
00457       if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) )
00458       {
00459         putConnection();
00460         m_openRequests--;
00461         printf( "Decrementing m_openRequests to %d\n", m_openRequests );
00462         std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength );
00463         m_parser.feed( xml );
00464         m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength );
00465         m_bufferContentLength = 0;
00466         m_bufferHeader = EmptyString;
00467       }
00468       else
00469       {
00470         printf( "buffer length mismatch\n" );
00471         break;
00472       }
00473     }
00474 
00475     printf( "~ConnectionBOSH::handleReceivedData()\n" );
00476   }
00477 
00478   void ConnectionBOSH::handleConnect( const ConnectionBase* connection )
00479   {
00480     printf( "New TCP connection %p established\n", connection );
00481     if( m_state == StateConnecting )
00482     {
00483       m_rid = rand() % 100000 + 1728679472;
00484 
00485       Tag requestBody( "body" );
00486       requestBody.setXmlns( XMLNS_HTTPBIND );
00487       requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" );
00488 
00489       requestBody.addAttribute( "content", "text/xml; charset=utf-8" );
00490       requestBody.addAttribute( "hold", (long)m_hold );
00491       requestBody.addAttribute( "rid", (long)m_rid );
00492       requestBody.addAttribute( "ver", "1.6" );
00493       requestBody.addAttribute( "wait", (long)m_wait );
00494       requestBody.addAttribute( "ack", 0 );
00495       requestBody.addAttribute( "secure", "false" );
00496       requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" );
00497       requestBody.addAttribute( "xml:lang", "en" );
00498       requestBody.addAttribute( "xmpp:version", "1.0" );
00499       requestBody.addAttribute( "to", m_server );
00500 
00501       m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending bosh connection request" );
00502       sendRequest( requestBody.xml() );
00503     }
00504   }
00505 
00506   void ConnectionBOSH::handleDisconnect( const ConnectionBase* connection,
00507                                          ConnectionError reason )
00508   {
00509     printf( "disconnected: %d\n", reason );
00510     if( m_handler && m_state == StateConnecting )
00511     {
00512       m_state = StateDisconnected;
00513       m_handler->handleDisconnect( this, reason );
00514       return;
00515     }
00516 
00517     switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose
00518     {
00519       case ModePipelining:
00520         m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining
00521         m_logInstance.dbg( LogAreaClassConnectionBOSH,
00522                            "connection closed - falling back to HTTP/1.0 connection method" );
00523         break;
00524       case ModeLegacyHTTP:
00525       case ModePersistentHTTP:
00526         // FIXME do we need to do anything here?
00527         printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason );
00528         break;
00529     }
00530   }
00531 
00532   void ConnectionBOSH::handleTag( Tag* tag )
00533   {
00534     if( !m_handler || tag->name() != "body" )
00535       return;
00536 
00537     if( m_streamRestart )
00538     {
00539       m_logInstance.dbg( LogAreaClassConnectionBOSH, "sending spoofed <stream:stream>" );
00540       m_handler->handleReceivedData( this, "<?xml version='1.0' ?>"
00541           "<stream:stream xmlns:stream='http://etherx.jabber.org/streams'"
00542           " xmlns='" + XMLNS_CLIENT + "' version='" + XMPP_STREAM_VERSION_MAJOR
00543           + "." + XMPP_STREAM_VERSION_MINOR + "' from='" + m_server + "' id ='"
00544           + m_sid + "' xml:lang='en'>" );
00545       m_streamRestart = false;
00546     }
00547 
00548     if( tag->hasAttribute( "sid" ) )
00549     {
00550       m_state = StateConnected;
00551       m_sid = tag->findAttribute( "sid" );
00552 
00553       if( tag->hasAttribute( "requests" ) )
00554       {
00555         const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() );
00556         if( serverRequests < m_maxOpenRequests )
00557         {
00558           m_maxOpenRequests = serverRequests;
00559           m_logInstance.dbg( LogAreaClassConnectionBOSH,
00560                               "bosh parameter 'requests' now set to " + tag->findAttribute( "requests" ) );
00561         }
00562       }
00563       if( tag->hasAttribute( "hold" ) )
00564       {
00565         const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() );
00566         if( maxHold < m_hold )
00567         {
00568           m_hold = maxHold;
00569           m_logInstance.dbg( LogAreaClassConnectionBOSH,
00570                               "bosh parameter 'hold' now set to " + tag->findAttribute( "hold" ) );
00571         }
00572       }
00573       if( tag->hasAttribute( "wait" ) )
00574       {
00575         const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() );
00576         if( maxWait < m_wait )
00577         {
00578           m_wait = maxWait;
00579           m_logInstance.dbg( LogAreaClassConnectionBOSH,
00580                               "bosh parameter 'wait' now set to " + tag->findAttribute( "wait" )
00581                                   + " seconds" );
00582         }
00583       }
00584       if( tag->hasAttribute( "polling" ) )
00585       {
00586         const int minTime = atoi( tag->findAttribute( "polling" ).c_str() );
00587         m_minTimePerRequest = minTime;
00588         m_logInstance.dbg( LogAreaClassConnectionBOSH,
00589                             "bosh parameter 'polling' now set to " + tag->findAttribute( "polling" )
00590                                 + " seconds" );
00591       }
00592 
00593       m_handler->handleConnect( this );
00594       m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" // FIXME move to send() so that
00595                                                                     // it is more clearly a response
00596                                                                     // to the initial stream opener?
00597                           "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' "
00598                           "xmlns='" + XMLNS_CLIENT
00599                           + "' version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR
00600                           + "' from='" + m_server + "' id ='" + m_sid + "' xml:lang='en'>" );
00601     }
00602 
00603     if( tag->findAttribute( "type" ) == "terminate" )
00604     {
00605       m_logInstance.dbg( LogAreaClassConnectionBOSH,
00606                          "bosh connection closed by server: " + tag->findAttribute( "condition" ) );
00607       m_state = StateDisconnected;
00608       m_handler->handleDisconnect( this, ConnStreamClosed );
00609       return;
00610     }
00611 
00612     const TagList& stanzas = tag->children();
00613     TagList::const_iterator it = stanzas.begin();
00614     for( ; it != stanzas.end(); ++it )
00615       m_handler->handleReceivedData( this, (*it)->xml() );
00616   }
00617 
00618   ConnectionBase* ConnectionBOSH::getConnection()
00619   {
00620     printf( "getConnection, before\n" );
00621     printf( "active connections:   %d\n", m_activeConnections.size() );
00622     printf( "inactive connections: %d\n", m_connectionPool.size() );
00623     if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests )
00624     {
00625       m_logInstance.warn( LogAreaClassConnectionBOSH,
00626                           "Too many requests already open. Cannot send." );
00627       return 0;
00628     }
00629 
00630     ConnectionBase* conn = 0;
00631     switch( m_connMode )
00632     {
00633       case ModePipelining:
00634         if( !m_activeConnections.empty() )
00635         {
00636           m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." );
00637           return m_activeConnections.front();
00638         }
00639         else if( !m_connectionPool.empty() )
00640         {
00641           m_logInstance.warn( LogAreaClassConnectionBOSH,
00642                               "Pipelining selected, but no connection open. Opening one." );
00643           return activateConnection();
00644         }
00645         else
00646           m_logInstance.warn( LogAreaClassConnectionBOSH,
00647                               "No available connections to pipeline on." );
00648         break;
00649       case ModeLegacyHTTP:
00650       case ModePersistentHTTP:
00651       {
00652         if( !m_connectionPool.empty() )
00653         {
00654           m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, "
00655                                                          "using connection from pool." );
00656           return activateConnection();
00657         }
00658         else if( !m_activeConnections.empty() )
00659         {
00660           m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." );
00661           conn = m_activeConnections.front()->newInstance();
00662           conn->registerConnectionDataHandler( this );
00663           m_connectionPool.push_back( conn );
00664           conn->connect();
00665         }
00666         else
00667           m_logInstance.warn( LogAreaClassConnectionBOSH,
00668                               "No available connections to send on." );
00669         break;
00670       }
00671     }
00672     return 0;
00673   }
00674 
00675   ConnectionBase* ConnectionBOSH::activateConnection()
00676   {
00677     ConnectionBase* conn = m_connectionPool.front();
00678     m_connectionPool.pop_front();
00679     if( conn->state() == StateConnected )
00680     {
00681       m_activeConnections.push_back( conn );
00682       printf( "activateConnection(1), after\n" );
00683       printf( "active connections:   %d\n", m_activeConnections.size() );
00684       printf( "inactive connections: %d\n", m_connectionPool.size() );
00685       return conn;
00686     }
00687 
00688     m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." );
00689     m_connectionPool.push_back( conn );
00690     conn->connect();
00691     printf( "activateConnection(2), after\n" );
00692     printf( "active connections:   %d\n", m_activeConnections.size() );
00693     printf( "inactive connections: %d\n", m_connectionPool.size() );
00694     return 0;
00695   }
00696 
00697   void ConnectionBOSH::putConnection()
00698   {
00699     printf( "putConnection, before\n" );
00700     printf( "active connections:   %d\n", m_activeConnections.size() );
00701     printf( "inactive connections: %d\n", m_connectionPool.size() );
00702 
00703     ConnectionBase* conn = m_activeConnections.front();
00704 
00705     switch( m_connMode )
00706     {
00707       case ModeLegacyHTTP:
00708         m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" );
00709         conn->disconnect();
00710         conn->cleanup(); // This is necessary
00711         m_activeConnections.pop_front();
00712         m_connectionPool.push_back( conn );
00713         break;
00714       case ModePersistentHTTP:
00715         m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" );
00716         m_activeConnections.pop_front();
00717         m_connectionPool.push_back( conn );
00718         break;
00719       case ModePipelining:
00720         m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" );
00721       default:
00722         break;
00723     }
00724     printf( "putConnection, after\n" );
00725     printf( "active connections:   %d\n", m_activeConnections.size() );
00726     printf( "inactive connections: %d\n", m_connectionPool.size() );
00727   }
00728 
00729 }

Generated on Mon Jun 23 10:50:17 2008 for gloox by  doxygen 1.4.1