1 /* 2 * Copyright (c) 2007-2019 by Jakob Schröter <js@camaya.net> 3 * This file is part of the gloox library. http://camaya.net/gloox 4 * 5 * This software is distributed under a license. The full license 6 * agreement can be found in the file LICENSE in this distribution. 7 * This software may not be copied, modified, sold or distributed 8 * other than expressed in the named license agreement. 9 * 10 * This software is distributed without any warranty. 11 */ 12 13 #include "config.h" 14 15 #include "gloox.h" 16 17 #include "connectionbosh.h" 18 #include "logsink.h" 19 #include "prep.h" 20 #include "tag.h" 21 #include "util.h" 22 23 #include <string> 24 #include <cstdlib> 25 #include <cctype> 26 #include <algorithm> 27 28 namespace gloox 29 { 30 ConnectionBOSH(ConnectionBase * connection,const LogSink & logInstance,const std::string & boshHost,const std::string & xmppServer,int xmppPort)31 ConnectionBOSH::ConnectionBOSH( ConnectionBase* connection, const LogSink& logInstance, 32 const std::string& boshHost, const std::string& xmppServer, 33 int xmppPort ) 34 : ConnectionBase( 0 ), 35 m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ), 36 m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ), 37 m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 1 ), m_streamRestart( false ), 38 m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ), 39 m_connMode( ModePipelining ) 40 { 41 initInstance( connection, xmppServer, xmppPort ); 42 } 43 ConnectionBOSH(ConnectionDataHandler * cdh,ConnectionBase * connection,const LogSink & logInstance,const std::string & boshHost,const std::string & xmppServer,int xmppPort)44 ConnectionBOSH::ConnectionBOSH( ConnectionDataHandler* cdh, ConnectionBase* connection, 45 const LogSink& logInstance, const std::string& boshHost, 46 const std::string& xmppServer, int xmppPort ) 47 : ConnectionBase( cdh ), 48 m_logInstance( logInstance ), m_parser( this ), m_boshHost( boshHost ), m_path( "/http-bind/" ), 49 m_rid( 0 ), m_initialStreamSent( false ), m_openRequests( 0 ), 50 m_maxOpenRequests( 2 ), m_wait( 30 ), m_hold( 1 ), m_streamRestart( false ), 51 m_lastRequestTime( std::time( 0 ) ), m_minTimePerRequest( 0 ), m_bufferContentLength( 0 ), 52 m_connMode( ModePipelining ) 53 { 54 initInstance( connection, xmppServer, xmppPort ); 55 } 56 initInstance(ConnectionBase * connection,const std::string & xmppServer,const int xmppPort)57 void ConnectionBOSH::initInstance( ConnectionBase* connection, const std::string& xmppServer, 58 const int xmppPort ) 59 { 60 // FIXME: check return value 61 prep::idna( xmppServer, m_server ); 62 m_port = xmppPort; 63 if( m_port != -1 ) 64 { 65 m_boshedHost = m_boshHost + ":" + util::int2string( m_port ); 66 } 67 68 // drop this connection into our pool of available connections 69 if( connection ) 70 { 71 connection->registerConnectionDataHandler( this ); 72 m_connectionPool.push_back( connection ); 73 } 74 } 75 ~ConnectionBOSH()76 ConnectionBOSH::~ConnectionBOSH() 77 { 78 util::clearList( m_activeConnections ); 79 util::clearList( m_connectionPool ); 80 } 81 newInstance() const82 ConnectionBase* ConnectionBOSH::newInstance() const 83 { 84 ConnectionBase* pBaseConn = 0; 85 86 if( !m_connectionPool.empty() ) 87 { 88 pBaseConn = m_connectionPool.front()->newInstance(); 89 } 90 else if( !m_activeConnections.empty() ) 91 { 92 pBaseConn = m_activeConnections.front()->newInstance(); 93 } 94 else 95 { 96 return 0; 97 } 98 99 return new ConnectionBOSH( m_handler, pBaseConn, m_logInstance, 100 m_boshHost, m_server, m_port ); 101 } 102 connect()103 ConnectionError ConnectionBOSH::connect() 104 { 105 if( m_state >= StateConnecting ) 106 return ConnNoError; 107 108 if( !m_handler ) 109 return ConnNotConnected; 110 111 m_state = StateConnecting; 112 m_logInstance.dbg( LogAreaClassConnectionBOSH, 113 "Initiating BOSH connection to server: " + 114 ( ( m_connMode == ModePipelining ) ? std::string( "Pipelining" ) 115 : ( ( m_connMode == ModeLegacyHTTP ) ? std::string( "LegacyHTTP" ) 116 : std::string( "PersistentHTTP" ) ) ) ); 117 getConnection(); 118 return ConnNoError; // FIXME? 119 } 120 disconnect()121 void ConnectionBOSH::disconnect() 122 { 123 if( ( m_connMode == ModePipelining && m_activeConnections.empty() ) 124 || ( m_connectionPool.empty() && m_activeConnections.empty() ) ) 125 return; 126 127 if( m_state != StateDisconnected ) 128 { 129 ++m_rid; 130 131 std::string requestBody = "<body rid='" + util::long2string( m_rid ) + "' "; 132 requestBody += "sid='" + m_sid + "' "; 133 requestBody += "type='terminal' "; 134 requestBody += "xml:lang='en' "; 135 requestBody += "xmlns='" + XMLNS_HTTPBIND + "'"; 136 if( m_sendBuffer.empty() ) // Make sure that any data in the send buffer gets sent 137 requestBody += "/>"; 138 else 139 { 140 requestBody += ">" + m_sendBuffer + "</body>"; 141 m_sendBuffer = EmptyString; 142 } 143 sendRequest( requestBody ); 144 145 m_logInstance.dbg( LogAreaClassConnectionBOSH, "BOSH disconnection request sent" ); 146 } 147 else 148 { 149 m_logInstance.err( LogAreaClassConnectionBOSH, 150 "Disconnecting from server in a non-graceful fashion" ); 151 } 152 153 util::ForEach( m_activeConnections, &ConnectionBase::disconnect ); 154 util::ForEach( m_connectionPool, &ConnectionBase::disconnect ); 155 156 m_state = StateDisconnected; 157 if( m_handler ) 158 m_handler->handleDisconnect( this, ConnUserDisconnected ); 159 } 160 recv(int timeout)161 ConnectionError ConnectionBOSH::recv( int timeout ) 162 { 163 ConnectionError ret = ConnNoError; 164 165 if( m_state == StateDisconnected ) 166 return ConnNotConnected; 167 168 if( !m_connectionPool.empty() ) 169 ret = m_connectionPool.front()->recv( 0 ); 170 if( !m_activeConnections.empty() ) 171 ret = m_activeConnections.front()->recv( timeout ); 172 173 // If there are no open requests then the spec allows us to send an empty request... 174 // (Some CMs do not obey this, it seems) 175 if( ( m_openRequests == 0 || m_sendBuffer.size() > 0 ) && m_state == StateConnected ) 176 { 177 m_logInstance.dbg( LogAreaClassConnectionBOSH, 178 "Sending empty request (or there is data in the send buffer)" ); 179 sendXML(); 180 } 181 182 return ret; 183 } 184 send(const std::string & data)185 bool ConnectionBOSH::send( const std::string& data ) 186 { 187 188 if( m_state == StateDisconnected ) 189 return false; 190 191 if( data.substr( 0, 2 ) == "<?" ) 192 { 193 // if( m_initialStreamSent ) 194 { 195 m_streamRestart = true; 196 sendXML(); 197 return true; 198 } 199 // else 200 // { 201 // m_initialStreamSent = true; 202 // m_logInstance.dbg( LogAreaClassConnectionBOSH, "Initial <stream:stream> dropped" ); 203 // return true; 204 // } 205 } 206 else if( data == "</stream:stream>" ) 207 return true; 208 209 m_sendBuffer += data; 210 sendXML(); 211 212 return true; 213 } 214 215 /* Sends XML. Wraps data in a <body/> tag, and then passes to sendRequest(). */ sendXML()216 bool ConnectionBOSH::sendXML() 217 { 218 if( m_state != StateConnected ) 219 { 220 m_logInstance.warn( LogAreaClassConnectionBOSH, 221 "Data sent before connection established (will be buffered)" ); 222 return false; 223 } 224 225 if( m_sendBuffer.empty() ) 226 { 227 time_t now = time( 0 ); 228 unsigned long delta = now - m_lastRequestTime; 229 if( delta < m_minTimePerRequest && m_openRequests > 0 ) 230 { 231 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Too little time between requests: " + util::long2string( delta ) + " seconds" ); 232 return false; 233 } 234 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Send buffer is empty, sending empty request" ); 235 } 236 237 ++m_rid; 238 239 std::string requestBody = "<body rid='" + util::long2string( m_rid ) + "' "; 240 requestBody += "sid='" + m_sid + "' "; 241 requestBody += "xmlns='" + XMLNS_HTTPBIND + "'"; 242 243 if( m_streamRestart ) 244 { 245 requestBody += " xmpp:restart='true' to='" + m_server + "' xml:lang='en' xmlns:xmpp='" 246 + XMLNS_XMPP_BOSH + "' />"; 247 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Restarting stream" ); 248 } 249 else 250 { 251 requestBody += ">" + m_sendBuffer + "</body>"; 252 } 253 // Send a request. Force if we are not sending an empty request, or if there are no connections open 254 if( sendRequest( requestBody ) ) 255 { 256 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Successfully sent m_sendBuffer" ); 257 m_sendBuffer = EmptyString; 258 m_streamRestart = false; 259 } 260 else 261 { 262 --m_rid; // I think... (may need to rethink when acks are implemented) 263 m_logInstance.warn( LogAreaClassConnectionBOSH, 264 "Unable to send. Connection not complete, or too many open requests," 265 " so added to buffer." ); 266 } 267 268 return true; 269 } 270 271 /* Chooses the appropriate connection, or opens a new one if necessary. Wraps xml in HTTP and sends. */ sendRequest(const std::string & xml)272 bool ConnectionBOSH::sendRequest( const std::string& xml ) 273 { 274 ConnectionBase* conn = getConnection(); 275 if( !conn ) 276 return false; 277 278 std::string request = "POST " + m_path; 279 if( m_connMode == ModeLegacyHTTP ) 280 { 281 request += " HTTP/1.0\r\n"; 282 request += "Connection: close\r\n"; 283 } 284 else 285 request += " HTTP/1.1\r\n"; 286 287 request += "Host: " + m_boshedHost + "\r\n"; 288 request += "Content-Type: text/xml; charset=utf-8\r\n"; 289 request += "Content-Length: " + util::long2string( xml.length() ) + "\r\n"; 290 request += "User-Agent: gloox/" + GLOOX_VERSION + "\r\n\r\n"; 291 request += xml; 292 293 294 if( conn->send( request ) ) 295 { 296 m_lastRequestTime = time( 0 ); 297 ++m_openRequests; 298 return true; 299 } 300 // else // FIXME What to do in this case? 301 // printf( "Error while trying to send on socket (state: %d)\n", conn->state() ); 302 303 return false; 304 } 305 ci_equal(char ch1,char ch2)306 bool ci_equal( char ch1, char ch2 ) 307 { 308 return std::toupper( ch1 ) == std::toupper( ch2 ); 309 } 310 ci_find(const std::string & str1,const std::string & str2)311 std::string::size_type ci_find( const std::string& str1, const std::string& str2 ) 312 { 313 std::string::const_iterator pos = std::search( str1.begin(), str1.end(), 314 str2.begin(), str2.end(), ci_equal ); 315 if( pos == str1.end() ) 316 return std::string::npos; 317 else 318 return std::distance( str1.begin(), pos ); 319 } 320 getHTTPField(const std::string & field)321 const std::string ConnectionBOSH::getHTTPField( const std::string& field ) 322 { 323 std::string::size_type fp = ci_find( m_bufferHeader, "\r\n" + field + ": " ); 324 325 if( fp == std::string::npos ) 326 return EmptyString; 327 328 fp += field.length() + 4; 329 330 const std::string::size_type fp2 = m_bufferHeader.find( "\r\n", fp ); 331 if( fp2 == std::string::npos ) 332 return EmptyString; 333 334 return m_bufferHeader.substr( fp, fp2 - fp ); 335 } 336 receive()337 ConnectionError ConnectionBOSH::receive() 338 { 339 ConnectionError err = ConnNoError; 340 while( m_state != StateDisconnected && ( err = recv( 10 ) ) == ConnNoError ) 341 ; 342 return err == ConnNoError ? ConnNotConnected : err; 343 } 344 cleanup()345 void ConnectionBOSH::cleanup() 346 { 347 m_state = StateDisconnected; 348 349 util::ForEach( m_activeConnections, &ConnectionBase::cleanup ); 350 util::ForEach( m_connectionPool, &ConnectionBase::cleanup ); 351 } 352 getStatistics(long int & totalIn,long int & totalOut)353 void ConnectionBOSH::getStatistics( long int& totalIn, long int& totalOut ) 354 { 355 util::ForEach( m_activeConnections, &ConnectionBase::getStatistics, totalIn, totalOut ); 356 util::ForEach( m_connectionPool, &ConnectionBase::getStatistics, totalIn, totalOut ); 357 } 358 handleReceivedData(const ConnectionBase *,const std::string & data)359 void ConnectionBOSH::handleReceivedData( const ConnectionBase* /*connection*/, 360 const std::string& data ) 361 { 362 m_buffer += data; 363 std::string::size_type headerLength = 0; 364 while( ( headerLength = m_buffer.find( "\r\n\r\n" ) ) != std::string::npos ) 365 { 366 m_bufferHeader = m_buffer.substr( 0, headerLength+2 ); 367 368 const std::string& statusCode = m_bufferHeader.substr( 9, 3 ); 369 if( statusCode != "200" ) 370 { 371 m_logInstance.warn( LogAreaClassConnectionBOSH, 372 "Received error via legacy HTTP status code: " + statusCode 373 + ". Disconnecting." ); 374 m_state = StateDisconnected; // As per XEP, consider connection broken 375 disconnect(); 376 } 377 378 m_bufferContentLength = atol( getHTTPField( "Content-Length" ).c_str() ); 379 if( !m_bufferContentLength ) 380 return; 381 382 if( m_connMode != ModeLegacyHTTP && ( getHTTPField( "Connection" ) == "close" 383 || m_bufferHeader.substr( 0, 8 ) == "HTTP/1.0" ) ) 384 { 385 m_logInstance.dbg( LogAreaClassConnectionBOSH, 386 "Server indicated lack of support for HTTP/1.1 - falling back to HTTP/1.0" ); 387 m_connMode = ModeLegacyHTTP; 388 } 389 390 if( m_buffer.length() >= ( headerLength + 4 + m_bufferContentLength ) ) 391 { 392 putConnection(); 393 --m_openRequests; 394 std::string xml = m_buffer.substr( headerLength + 4, m_bufferContentLength ); 395 m_parser.feed( xml ); 396 m_buffer.erase( 0, headerLength + 4 + m_bufferContentLength ); 397 m_bufferContentLength = 0; 398 m_bufferHeader = EmptyString; 399 } 400 else 401 { 402 m_logInstance.warn( LogAreaClassConnectionBOSH, "Buffer length mismatch" ); 403 break; 404 } 405 } 406 } 407 handleConnect(const ConnectionBase *)408 void ConnectionBOSH::handleConnect( const ConnectionBase* /*connection*/ ) 409 { 410 if( m_state == StateConnecting ) 411 { 412 m_rid = rand() % 100000 + 1728679472; 413 414 Tag requestBody( "body" ); 415 requestBody.setXmlns( XMLNS_HTTPBIND ); 416 requestBody.setXmlns( XMLNS_XMPP_BOSH, "xmpp" ); 417 418 requestBody.addAttribute( "content", "text/xml; charset=utf-8" ); 419 requestBody.addAttribute( "hold", m_hold ); 420 requestBody.addAttribute( "rid", static_cast<long>( m_rid ) ); 421 requestBody.addAttribute( "ver", "1.6" ); 422 requestBody.addAttribute( "wait", m_wait ); 423 requestBody.addAttribute( "ack", 0 ); 424 requestBody.addAttribute( "secure", "false" ); 425 requestBody.addAttribute( "route", "xmpp:" + m_server + ":5222" ); 426 requestBody.addAttribute( "xml:lang", "en" ); 427 requestBody.addAttribute( "xmpp:version", "1.0" ); 428 requestBody.addAttribute( "to", m_server ); 429 430 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Sending BOSH connection request" ); 431 sendRequest( requestBody.xml() ); 432 } 433 } 434 handleDisconnect(const ConnectionBase *,ConnectionError reason)435 void ConnectionBOSH::handleDisconnect( const ConnectionBase* /*connection*/, 436 ConnectionError reason ) 437 { 438 if( m_handler && m_state == StateConnecting ) 439 { 440 m_state = StateDisconnected; 441 m_handler->handleDisconnect( this, reason ); 442 return; 443 } 444 445 switch( m_connMode ) // FIXME avoid that if we're disconnecting on purpose 446 { 447 case ModePipelining: 448 m_connMode = ModeLegacyHTTP; // Server seems not to support pipelining 449 m_logInstance.dbg( LogAreaClassConnectionBOSH, 450 "Connection closed - falling back to HTTP/1.0 connection method" ); 451 break; 452 case ModeLegacyHTTP: 453 case ModePersistentHTTP: 454 // FIXME do we need to do anything here? 455 // printf( "A TCP connection %p was disconnected (reason: %d).\n", connection, reason ); 456 break; 457 } 458 } 459 handleTag(Tag * tag)460 void ConnectionBOSH::handleTag( Tag* tag ) 461 { 462 if( !m_handler || tag->name() != "body" ) 463 return; 464 465 if( m_streamRestart ) 466 { 467 m_streamRestart = false; 468 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Sending spoofed <stream:stream>" ); 469 m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" 470 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams'" 471 " xmlns='" + XMLNS_CLIENT + "' version='" + XMPP_STREAM_VERSION_MAJOR 472 + "." + XMPP_STREAM_VERSION_MINOR + "' from='" + m_server + "' id ='" 473 + m_sid + "' xml:lang='en'>" ); 474 } 475 476 if( tag->hasAttribute( "sid" ) ) 477 { 478 m_state = StateConnected; 479 m_sid = tag->findAttribute( "sid" ); 480 481 if( tag->hasAttribute( "requests" ) ) 482 { 483 const int serverRequests = atoi( tag->findAttribute( "requests" ).c_str() ); 484 if( serverRequests < m_maxOpenRequests ) 485 { 486 m_maxOpenRequests = serverRequests; 487 m_logInstance.dbg( LogAreaClassConnectionBOSH, 488 "BOSH parameter 'requests' now set to " + tag->findAttribute( "requests" ) ); 489 } 490 } 491 if( tag->hasAttribute( "hold" ) ) 492 { 493 const int maxHold = atoi( tag->findAttribute( "hold" ).c_str() ); 494 if( maxHold < m_hold ) 495 { 496 m_hold = maxHold; 497 m_logInstance.dbg( LogAreaClassConnectionBOSH, 498 "BOSH parameter 'hold' now set to " + tag->findAttribute( "hold" ) ); 499 } 500 } 501 if( tag->hasAttribute( "wait" ) ) 502 { 503 const int maxWait = atoi( tag->findAttribute( "wait" ).c_str() ); 504 if( maxWait < m_wait ) 505 { 506 m_wait = maxWait; 507 m_logInstance.dbg( LogAreaClassConnectionBOSH, 508 "BOSH parameter 'wait' now set to " + tag->findAttribute( "wait" ) 509 + " seconds" ); 510 } 511 } 512 if( tag->hasAttribute( "polling" ) ) 513 { 514 const int minTime = atoi( tag->findAttribute( "polling" ).c_str() ); 515 m_minTimePerRequest = minTime; 516 m_logInstance.dbg( LogAreaClassConnectionBOSH, 517 "BOSH parameter 'polling' now set to " + tag->findAttribute( "polling" ) 518 + " seconds" ); 519 } 520 521 if( m_state < StateConnected ) 522 m_handler->handleConnect( this ); 523 524 m_handler->handleReceivedData( this, "<?xml version='1.0' ?>" // FIXME move to send() so that 525 // it is more clearly a response 526 // to the initial stream opener? 527 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' " 528 "xmlns='" + XMLNS_CLIENT 529 + "' version='" + XMPP_STREAM_VERSION_MAJOR + "." + XMPP_STREAM_VERSION_MINOR 530 + "' from='" + m_server + "' id ='" + m_sid + "' xml:lang='en'>" ); 531 } 532 533 if( tag->findAttribute( "type" ) == "terminate" ) 534 { 535 m_logInstance.dbg( LogAreaClassConnectionBOSH, 536 "BOSH connection closed by server: " + tag->findAttribute( "condition" ) ); 537 m_state = StateDisconnected; 538 m_handler->handleDisconnect( this, ConnStreamClosed ); 539 return; 540 } 541 542 const TagList& stanzas = tag->children(); 543 TagList::const_iterator it = stanzas.begin(); 544 for( ; it != stanzas.end(); ++it ) 545 m_handler->handleReceivedData( this, (*it)->xml() ); 546 } 547 getConnection()548 ConnectionBase* ConnectionBOSH::getConnection() 549 { 550 if( m_openRequests > 0 && m_openRequests >= m_maxOpenRequests ) 551 { 552 m_logInstance.warn( LogAreaClassConnectionBOSH, 553 "Too many requests already open. Cannot send." ); 554 return 0; 555 } 556 557 ConnectionBase* conn = 0; 558 switch( m_connMode ) 559 { 560 case ModePipelining: 561 if( !m_activeConnections.empty() ) 562 { 563 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Using default connection for Pipelining." ); 564 return m_activeConnections.front(); 565 } 566 else if( !m_connectionPool.empty() ) 567 { 568 m_logInstance.warn( LogAreaClassConnectionBOSH, 569 "Pipelining selected, but no connection open. Opening one." ); 570 return activateConnection(); 571 } 572 else 573 m_logInstance.warn( LogAreaClassConnectionBOSH, 574 "No available connections to pipeline on." ); 575 break; 576 case ModeLegacyHTTP: 577 case ModePersistentHTTP: 578 { 579 if( !m_connectionPool.empty() ) 580 { 581 m_logInstance.dbg( LogAreaClassConnectionBOSH, "LegacyHTTP/PersistentHTTP selected, " 582 "using connection from pool." ); 583 return activateConnection(); 584 } 585 else if( !m_activeConnections.empty() ) 586 { 587 m_logInstance.dbg( LogAreaClassConnectionBOSH, "No connections in pool, creating a new one." ); 588 conn = m_activeConnections.front()->newInstance(); 589 conn->registerConnectionDataHandler( this ); 590 m_connectionPool.push_back( conn ); 591 conn->connect(); 592 } 593 else 594 m_logInstance.warn( LogAreaClassConnectionBOSH, 595 "No available connections to send on." ); 596 break; 597 } 598 } 599 return 0; 600 } 601 activateConnection()602 ConnectionBase* ConnectionBOSH::activateConnection() 603 { 604 ConnectionBase* conn = m_connectionPool.front(); 605 m_connectionPool.pop_front(); 606 if( conn->state() == StateConnected ) 607 { 608 m_activeConnections.push_back( conn ); 609 return conn; 610 } 611 612 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Connecting pooled connection." ); 613 m_connectionPool.push_back( conn ); 614 conn->connect(); 615 return 0; 616 } 617 putConnection()618 void ConnectionBOSH::putConnection() 619 { 620 ConnectionBase* conn = m_activeConnections.front(); 621 622 switch( m_connMode ) 623 { 624 case ModeLegacyHTTP: 625 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Disconnecting LegacyHTTP connection" ); 626 conn->disconnect(); 627 conn->cleanup(); // This is necessary 628 m_activeConnections.pop_front(); 629 m_connectionPool.push_back( conn ); 630 break; 631 case ModePersistentHTTP: 632 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Deactivating PersistentHTTP connection" ); 633 m_activeConnections.pop_front(); 634 m_connectionPool.push_back( conn ); 635 break; 636 case ModePipelining: 637 m_logInstance.dbg( LogAreaClassConnectionBOSH, "Keeping Pipelining connection" ); 638 default: 639 break; 640 } 641 } 642 643 } 644