/home/croftj/photogrotto/QcjHttpService.cpp

00001 /*********************************************************************************
00002 **
00003 **   $Id: //depot/WorkInProgress/QcjData/QcjHttpService.cpp#8 $
00004 **   Copyright (c) 2006 Joe Croft joe@croftj.net
00005 **   
00006 **   This file is part of Qcj Classes.
00007 **
00008 **   QcjHttpService.cpp is free software; you can redistribute it and/or modify
00009 **   it under the terms of the GNU General Public License as published by
00010 **   the Free Software Foundation; either version 2 of the License, or
00011 **   (at your option) any later version.
00012 **
00013 **   Foobar is distributed in the hope that it will be useful,
00014 **   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 **   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 **   GNU General Public License for more details.
00017 **
00018 **   You should have received a copy of the GNU General Public License
00019 **   along with Foobar; if not, write to the Free Software
00020 **   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021 **
00022 *********************************************************************************/
00023 
00024 # include "QcjHttpService.h"
00025 
00026 /*!
00027        \class QcjHttpService
00028        
00029        \brief Http protocol service
00030        
00031        This  class  is a base class for providing core functionalitity
00032        for  an  http service. It provides basic protocol packaging and
00033        unpackaging.  It  is  expected  to be subclassed to provide the
00034        actual  functionality.
00035 
00036        The actual processing of requests are performed on a seperate thread 
00037        from the main application. While the new thread which is created
00038        for this object handles parsing the requests, processing then and 
00039        formulating appropriate responses, the main application thread
00040        is still responsible for handling the basic IO for the object. The 
00041        passing of data back and forth from the IO and this object is done
00042        through signals and slots and a couple of buffers. This is mandated
00043        by the fact that the Qt library cannot handle socket operations to be
00044        called by a thread from which the socket wasn't created under.
00045 */
00046 
00047 
00048 /*!
00049        \fn     QcjHttpService::QcjHttpService(int    socketDescripter,
00050        QObject  *parent,  int max_req, QStringList *extraMethods, long
00051        ttl)
00052        
00053        This  is  the constructor. It creates it's QThread object, then
00054        sets  up  to  handle  requests  from the socket whose number is
00055        passed  in  the parameter \a socketDescripter. The parameter \a
00056        max_req  specifies  how many requests the service should handle
00057        before  quiting.  The  parameter \a ttl specified how long thie
00058        service should stay a live in milliseconds.
00059        
00060        By  default,  the  HTTP  methods  GET,  POST, PUT, HEAD, TRACE,
00061        OPTIONS   and   DELETE  are  recognized.  For  flexibility  and
00062        custimization,  additional  methods  may be passed in using the
00063        parameter \a extraMethods.
00064 */ 
00065 
00066 QcjHttpService::QcjHttpService(int socketDescripter, QObject *parent, int max_req, QStringList *extraMethods, long ttl) : 
00067    QThread(parent)
00068 {
00069    printf("QcjHttpService::QcjHttpService(%s): Enter, socknum = %d\n", qPrintable(QString::number(currentThreadId())), socknum);
00070    fflush(stdout);
00071    if ( socketDescripter >= 0 ) 
00072    {
00073       socknum = socketDescripter;
00074 
00075       timeToLive = ttl;
00076       maxRequests = max_req;
00077       methods << "GET";
00078       methods << "POST";
00079       methods << "PUT";
00080       methods << "HEAD";
00081       methods << "TRACE";
00082       methods << "OPTIONS";
00083       methods << "DELETE";
00084       if ( extraMethods != 0 ) 
00085          methods << *extraMethods;
00086 
00087       if ((sock = new QTcpSocket()) == NULL)
00088       {
00089          printf("QcjHttpService::QcjHttpService(%s): Error creating socket\n", qPrintable(QString::number(currentThreadId())));
00090          fflush(stdout);
00091          return;
00092       }
00093       printf("QcjHttpService::QcjHttpService(%s): setting descripter\n", qPrintable(QString::number(currentThreadId())));
00094       fflush(stdout);
00095       if (! sock->setSocketDescriptor(socknum)) {
00096          printf("QcjHttpService::QcjHttpService(%s): Exit in error\n", qPrintable(QString::number(currentThreadId())));
00097          fflush(stdout);
00098          return;
00099       }
00100       printf("QcjHttpService::QcjHttpService(%s): Connecting signals and handles\n", qPrintable(QString::number(currentThreadId())));
00101       fflush(stdout);
00102       connect(sock, SIGNAL(readyRead()),                          this, SLOT(haveData()), Qt::QueuedConnection);
00103       connect(sock, SIGNAL(disconnected()),                       this, SLOT(haveDisconnect()), Qt::QueuedConnection);
00104       connect(sock, SIGNAL(error(QAbstractSocket::SocketError)),  this, SLOT(haveError(QAbstractSocket::SocketError)));
00105 
00106       inBuffer.setBuffer(&inArray);
00107       inBuffer.open(QBuffer::ReadWrite);
00108       inStream.setDevice(&inBuffer);
00109 //      inStream = new QDataStream(&inBuffer);
00110       inBody = false;
00111       request = 0;
00112    }
00113    printf("QcjHttpService::QcjHttpService(%s): exit\n", qPrintable(QString::number(currentThreadId())));
00114    fflush(stdout);
00115 }
00116 
00117 /*!
00118        \fn  run()
00119        
00120        This function should not be called directly.
00121        
00122        This  function is called when the start() function is called for
00123        the  object.  The thread remains alive as long as this function
00124        does not exit. Once run exits, the thread is killed.
00125 */ 
00126 void QcjHttpService::run()
00127 {
00128    QString method;
00129    QString path;
00130    QString version;
00131    bool err = false;
00132 
00133    printf("QcjHttpService::run(%s): Enter, socknum = %d\n", qPrintable(QString::number(currentThreadId())), socknum);
00134    fflush(stdout);
00135 
00136    ttlTimer = new QTimer;
00137    connect(ttlTimer, SIGNAL(timeout()), this, SLOT(haveTimeOut()));
00138    ttlTimer->start(timeToLive);
00139 
00140    errTimer = new QTimer;
00141    connect(errTimer, SIGNAL(timeout()), this, SLOT(haveTimeOut()));
00142 
00143    exitFlag = false;
00144    myThreadId = currentThreadId();
00145    lineBuffer.clear();
00146    bufLock.lock();
00147    while (1) 
00148    {
00149       if ( exitFlag ) 
00150       {
00151          printf("QcjHttpService::run(%s): exit flag set, exiting!\n", qPrintable(QString::number(currentThreadId())));
00152          fflush(stdout);
00153          clearLocks();
00154          printf("QcjHttpService::run(%s): Calling return\n", qPrintable(QString::number(currentThreadId())));
00155          fflush(stdout);
00156          return;
00157       }
00158 
00159       if ( inArray.size() == 0 )
00160       {
00161          printf("QcjHttpService::run(%s): waiting input, sock %d\n", qPrintable(QString::number(currentThreadId())), socknum);
00162          fflush(stdout);
00163          haveInput.wait(&bufLock);
00164       }
00165 
00166       if ( exitFlag ) 
00167       {
00168          printf("QcjHttpService::run(%s): exit flag set, exiting!\n", qPrintable(QString::number(currentThreadId())));
00169          fflush(stdout);
00170          clearLocks();
00171          printf("QcjHttpService::run(%s): Calling return\n", qPrintable(QString::number(currentThreadId())));
00172          fflush(stdout);
00173          return;
00174       }
00175 
00176       if ( ! inBody ) 
00177       {
00178          printf("QcjHttpService::run(%s): Not in body, have %d bytes available\n", qPrintable(QString::number(currentThreadId())), inArray.size());
00179          fflush(stdout);
00180 //         while ( inBuffer.bytesAvailable() > 0 ) 
00181          while ( inArray.size() > 0 )
00182          {
00183             char ch;
00184 //            printf("QcjHttpService::run(%s): reading char\n", qPrintable(QString::number(currentThreadId())));
00185 //            fflush(stdout);
00186             ch = *(inArray.data());
00187             inArray.remove(0, 1);
00188             bufLock.unlock();
00189 //            printf("QcjHttpService::run(%s): got char 0x%02x\n", qPrintable(QString::number(currentThreadId())), ch);
00190 //            fflush(stdout);
00191             lineBuffer += ch;
00192             if ( ch == '\0' ) 
00193             {
00194                printf("QcjHttpService::run(%s): have null byte\n", qPrintable(QString::number(currentThreadId())));
00195                fflush(stdout);
00196                bufLock.lock();
00197 //               printf("QcjHttpService::run(%s): Checking for more bytes, %d bytes available\n", qPrintable(QString::number(currentThreadId())), inArray.size());
00198 //               fflush(stdout);
00199                continue;
00200             }
00201 
00202             if ( ch == '\n' )
00203             {
00204                printf("QcjHttpService::run(%s): Have line: |%s|\n", qPrintable(QString::number(currentThreadId())), qPrintable(lineBuffer));
00205                fflush(stdout);
00206                errTimer->start(500);
00207                QString in;
00208                in.append(lineBuffer);
00209                lineBuffer.clear();
00210                in.remove('\r');
00211                in.remove('\n');
00212                if ( request == 0 ) 
00213                {
00214                   printf("QcjHttpService::run(%s): Processing request\n", qPrintable(QString::number(currentThreadId())));
00215                   fflush(stdout);
00216                   request = new QMap<QString, QVariant>;
00217 
00218                   method.clear();
00219                   path.clear();
00220                   version.clear();
00221                   QStringList sl = in.split(" ");
00222 
00223                   printf("QcjHttpService::run(%s): Have %d request parts\n", qPrintable(QString::number(currentThreadId())), sl.size());
00224                   fflush(stdout);
00225 
00226                   if ( sl.size() > 0 ) 
00227                      method = sl[0];
00228                   if ( sl.size() > 1 ) 
00229                      path = sl[1];
00230                   if ( sl.size() > 2 ) 
00231                      version = sl[2];
00232                   else 
00233                      err = true;
00234 
00235                   printf("QcjHttpService::run(%s): method = |%s|\n", qPrintable(QString::number(currentThreadId())), qPrintable(method));
00236                   fflush(stdout);
00237                   if ( err ) 
00238                   {
00239                      printf("QcjHttpService::run(%s): Returning error and exiting\n", qPrintable(QString::number(currentThreadId())));
00240                      fflush(stdout);
00241                      errorResponse(QcjHttpService::BadRequest, "Malformed Request");
00242                      delete request;
00243                      request = 0;
00244                   }
00245                   else if ( methods.contains(method) )
00246                   {
00247                      printf("QcjHttpService::run(%s): Have valid method\n", qPrintable(QString::number(currentThreadId())));
00248                      fflush(stdout);
00249                      QString args("");
00250                      QString resource("");
00251                      QStringList reslist = path.split("?");
00252                      if ( reslist.size() == 2 ) 
00253                      {
00254                         resource = reslist[0];
00255                         args = reslist[1];
00256                      }
00257                      else
00258                         resource = path;
00259 
00260                      request->insert("Resource", resource);
00261                      request->insert("Arguments", args);
00262                      request->insert("Method", method);
00263                      request->insert("Version", version);
00264                      request->insert("Content-Length", QVariant((qlonglong)0));
00265                   }
00266                   else
00267                   {
00268                      errorResponse(QcjHttpService::BadRequest, "Invalid method: " + method);
00269                      delete request;
00270                      request = 0;
00271                   }
00272                }
00273                else if ( in.size() > 0 )
00274                {
00275                   QRegExp re("(.+): (.*)");
00276                   re.setMinimal(true);
00277 
00278                   printf("QcjHttpService::run(%s): Testing for tag\n", qPrintable(QString::number(currentThreadId())));
00279                   fflush(stdout);
00280                   QStringList sl = in.split(": ");
00281                   if ( sl.size() == 1 ) 
00282                   {
00283                      sl = in.split(":");
00284                   }
00285                   if ( sl.size() == 2 )
00286                   {
00287                      QString tag = sl[0];
00288                      QString val = sl[1];
00289                      printf("QcjHttpService::run(%s): tag = |%s|, val = |%s|\n", qPrintable(QString::number(currentThreadId())), qPrintable(tag), qPrintable(val));
00290                      fflush(stdout);
00291                      bool isint;
00292                      long x = val.toLong(&isint);
00293                      if ( isint ) 
00294                         request->insert(tag, (qlonglong)x);
00295                      else
00296                         request->insert(tag, val);
00297                   }
00298                }
00299                else 
00300                {
00301                   expectingBytes = request->value("Content-Length").toLongLong();
00302                   printf("QcjHttpService::run(%s): content length = %ld\n", qPrintable(QString::number(currentThreadId())), (long)expectingBytes);
00303                   fflush(stdout);
00304                   if ( expectingBytes == 0 ) 
00305                   {
00306                      printf("QcjHttpService::run(%s): Not getting any data\n", qPrintable(QString::number(currentThreadId())));
00307                      fflush(stdout);
00308                      QMap<QString, QVariant> response;
00309                      errTimer->stop();
00310                      printf("QcjHttpService::run(%s): Calling processRequest()\n", qPrintable(QString::number(currentThreadId())));
00311                      fflush(stdout);
00312                      processRequest(request, &response);
00313                      processResponse(response);
00314                      printf("QcjHttpService::run(%s): Clearing bufs etc.()\n", qPrintable(QString::number(currentThreadId())));
00315                      fflush(stdout);
00316                      delete request;
00317                      request = 0;
00318                   }
00319                   else
00320                   {
00321                      inBody = true;
00322                      break;
00323                   }
00324                }
00325             }
00326 //            printf("QcjHttpService::run(%s): locking buffer\n", qPrintable(QString::number(currentThreadId())));
00327 //            fflush(stdout);
00328             bufLock.lock();
00329          }
00330       }
00331 
00332       if ( inBody  && inArray.count() > 0 ) 
00333       {
00334       /*
00335          char *buf = new char(expectingBytes);
00336          if ( (len = inBuffer.left(buf, expectingBytes)) > 0 ) 
00337          {
00338             bufLock.unlock();
00339             expectingBytes -= len;
00340             inStream.writeBytes(buf, len);
00341             if ( expectingBytes == 0 ) 
00342             {
00343                request->insert("Request-Body", QVariant(inArray));
00344                QMap<QString, QVariant> response;
00345                ttlTimer->stop();
00346                processRequest(request, &response);
00347                delete request;
00348                request = 0;
00349                inBody = false;               
00350             }
00351             bufLock.lock();
00352          }
00353          delete buf;
00354       */
00355       }
00356       printf("QcjHttpService::run(%s): waiting for more data to come in\n", qPrintable(QString::number(currentThreadId())));
00357       fflush(stdout);
00358    }
00359    printf("QcjHttpService::run(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00360    fflush(stdout);
00361 }
00362 
00363 /*!
00364        \fn void QcjHttpService::haveData()
00365        
00366        This  function  recieves  incomming data from the socket. It is
00367        not  actually executed as part of the thread, but by the thread
00368        the socket was created in.
00369 */ 
00370 void QcjHttpService::haveData()
00371 {
00372    printf("QcjHttpService::haveData(%s): Enter bytes avail = %ld\n", qPrintable(QString::number(currentThreadId())), (long)sock->bytesAvailable());
00373    fflush(stdout);
00374    while (sock->bytesAvailable() > 0) 
00375    {
00376       char buf[1024];
00377       int bytes;
00378       if ( (bytes = sock->read(buf, sizeof(buf))) > 0 )
00379       {
00380          bufLock.lock();
00381          printf("QcjHttpService::haveData(%s): appending %d bytes () to inArray\n", qPrintable(QString::number(currentThreadId())), bytes);
00382          fflush(stdout);
00383 //         int x = inStream.writeRawData(buf, bytes);
00384          inArray.append(QByteArray(buf, bytes));
00385          printf("QcjHttpService::haveData(%s): inArray has %d bytes available\n", qPrintable(QString::number(currentThreadId())), inArray.size());
00386          fflush(stdout);
00387          bufLock.unlock();
00388       }
00389       else if ( bytes < 0 )
00390          return;
00391    }
00392    printf("QcjHttpService::haveData(%s): waking thread\n", qPrintable(QString::number(currentThreadId())));
00393    fflush(stdout);
00394    haveInput.wakeAll();
00395    printf("QcjHttpService::haveData(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00396    fflush(stdout);
00397 }
00398 
00399 
00400 /*!
00401        \fn      void     QcjHttpService::processResponse(QMap<QString,
00402        QVariant> rsp)
00403        
00404        This  function  processes  responses and sends them out. If the
00405        maximum  number  of  requests have been fulfilled, it will emit
00406        the signal closeSocket triggering this threads ultimate demise.
00407 */ 
00408 void QcjHttpService::processResponse(QMap<QString, QVariant> rsp)
00409 {
00410    printf("QcjHttpService::processResponse(%s): Enter\n", qPrintable(QString::number(currentThreadId())));
00411    fflush(stdout);
00412    sendResponse(&rsp);
00413 
00414    printf("QcjHttpService::processResponse(%s): maxRequests = %d\n", qPrintable(QString::number(currentThreadId())), maxRequests);
00415    fflush(stdout);
00416    if ( --maxRequests == 0) 
00417    {
00418       printf("QcjHttpService::processResponse(%s): terminating socket connection\n", qPrintable(QString::number(currentThreadId())));
00419       fflush(stdout);
00420       emit closeSocket(sock);
00421    }
00422    printf("QcjHttpService::processResponse(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00423    fflush(stdout);
00424 }
00425 
00426 /*!
00427        \fn  void  QcjHttpService::sendResponse(QMap<QString, QVariant>
00428        *rsp)
00429        
00430        This  function  formulates the actual response and emits a send
00431        signal to have it written to the socket.
00432 */ 
00433 void QcjHttpService::sendResponse(QMap<QString, QVariant> *rsp)
00434 {
00435    QByteArray body;
00436 
00437    printf("QcjHttpService::sendResponse(%s): Enter\n", qPrintable(QString::number(currentThreadId())));
00438    fflush(stdout);
00439 
00440    QMapIterator<QString, QVariant> i(*rsp);
00441    while (i.hasNext()) 
00442    {
00443       i.next();
00444       if ( i.key() == "Request-Body" ) 
00445          printf("DbPhotoService::sendResponse(%s): rsp: |%s| %d length\n", qPrintable(QString::number(currentThreadId())), qPrintable(i.key()), i.value().toByteArray().size());
00446       else
00447          printf("DbPhotoService::sendResponse(%s): rsp: |%s| = |%s|\n", qPrintable(QString::number(currentThreadId())), qPrintable(i.key()), qPrintable(i.value().toString()));
00448       fflush(stdout);
00449    }
00450 
00451    if ( rsp->value("Status").toInt() != QcjHttpService::OK ) 
00452    {
00453       printf("QcjHttpService::sendResponse(%s): Sending error response\n", qPrintable(QString::number(currentThreadId())));
00454       fflush(stdout);
00455       emit send(sock, qPrintable("HTTP/1.1 " + rsp->value("Status").toString() + " " + rsp->value("StatusString").toString() + "\r\n"));
00456       printf("QcjHttpService::sendResponse(%s): returning\n", qPrintable(QString::number(currentThreadId())));
00457       fflush(stdout);
00458       return;
00459    }
00460 
00461    printf("QcjHttpService::sendResponse(%s): Sending regular response\n", qPrintable(QString::number(currentThreadId())));
00462    fflush(stdout);
00463    emit send(sock, qPrintable("HTTP/1.1 " + rsp->value("Status").toString() + " " + rsp->value("StatusString").toString() + "\r\n"));
00464 
00465    printf("QcjHttpService::sendResponse(%s): Iterating headers\n", qPrintable(QString::number(currentThreadId())));
00466    fflush(stdout);
00467    QMapIterator<QString, QVariant> it(*rsp);
00468    while (it.hasNext()) 
00469    {
00470       it.next();
00471       printf("QcjHttpService::sendResponse(%s): fetching key\n", qPrintable(QString::number(currentThreadId())));
00472       fflush(stdout);
00473       QString tag = it.key();
00474       if ( tag == "Request-Body" ) 
00475       {
00476          printf("QcjHttpService::sendResponse(%s): Setting body\n", qPrintable(QString::number(currentThreadId())));
00477          fflush(stdout);
00478          body = it.value().toByteArray();
00479       }
00480       else 
00481       {
00482          printf("QcjHttpService::sendResponse(%s): Sending tag\n", qPrintable(QString::number(currentThreadId())));
00483          fflush(stdout);
00484          QString val = it.value().toString();
00485          emit send(sock, qPrintable(tag + ": " + val + "\r\n"));
00486       }
00487    }
00488    printf("QcjHttpService::sendResponse(%s): Sending body\n", qPrintable(QString::number(currentThreadId())));
00489    fflush(stdout);
00490 
00491    if ( body.size() > 0 ) 
00492    {
00493       emit send(sock, "\r\n");
00494       emit send(sock, body);
00495    }
00496    printf("QcjHttpService::sendResponse(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00497    fflush(stdout);
00498 }
00499 
00500 /*!
00501       \fn void QcjHttpService::haveTimeout()
00502 
00503        This  slot is called when senders are too slow sending in their
00504        requests. It generates a error.
00505 */ 
00506 void QcjHttpService::haveTimeout()
00507 {
00508    errorResponse(QcjHttpService::RequestTimeout, "You're too slow!");
00509 }
00510 
00511 /*!
00512       \fn void QcjHttpService::haveError(QAbstractSocket::SocketError err)
00513        This slot is called when an error is detected on the socket. It
00514        sets up to terminate the thread.
00515 */ 
00516 void QcjHttpService::haveError(QAbstractSocket::SocketError)
00517 {
00518    printf("QcjHttpService::haveError(%s): Enter, flagging thread %s to exit\n", qPrintable(QString::number(currentThreadId())), qPrintable(QString::number(myThreadId)));
00519    fflush(stdout);
00520    exitFlag = true;
00521    haveInput.wakeAll();
00522    printf("QcjHttpService::haveError(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00523    fflush(stdout);
00524 }
00525 
00526 
00527 /*!
00528        \fn  void  QcjHttpService::haveDisconnect() This slot is called
00529        when the socket closes. It sets up to terminate the thread.
00530 */ 
00531 void QcjHttpService::haveDisconnect()
00532 {
00533    printf("QcjHttpService::haveDisconnect(%s): Enter, flagging thread %s to exit\n", qPrintable(QString::number(currentThreadId())), qPrintable(QString::number(myThreadId)));
00534    fflush(stdout);
00535    exitFlag = true;
00536    haveInput.wakeAll();
00537    printf("QcjHttpService::haveDisconnect(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00538    fflush(stdout);
00539 }
00540 
00541 /*!
00542       \fn void QcjHttpService::errorResponse(enum ErrorCodes ec, QString msg)
00543 
00544        This function creates an error response and sends it out
00545 */ 
00546 void QcjHttpService::errorResponse(enum ErrorCodes ec, QString msg)
00547 {
00548    QMap<QString, QVariant> responses;
00549    responses.insert("Status", QString::number(ec));
00550    responses.insert("StatusString", msg);
00551    sendResponse(&responses);
00552    emit closeSocket(sock);
00553 }
00554 
00555 /*!
00556        \fn                    QMap<QString,                   QString>
00557        QcjHttpService::parseArguments(QString args)
00558        
00559        This function parses the incomming request string and returns a
00560        map of arguments and their values as a QMap.
00561 */ 
00562 QMap<QString, QString> QcjHttpService::parseArguments(QString args)
00563 {
00564    QMap<QString, QString> rv;
00565    QStringList sl = args.split("&");
00566    for (int x = 0; x < sl.size(); x++) 
00567    {
00568       QStringList sl1 = sl[x].split("=");
00569       if ( sl1.size() == 2 ) 
00570          rv.insert(sl1[0], sl1[1]);
00571    }
00572    return(rv);
00573 }
00574 
00575 /*!
00576        \fn void QcjHttpService::haveTimeOut()
00577        
00578        This  is  called  when  the  ttl  time  expires.  It  emits the
00579        closeSocket() signal triggering the threads demise.
00580 */ 
00581 void QcjHttpService::haveTimeOut()
00582 {
00583    printf("QcjHttpService::haveTimeOut(%s): Enter\n", qPrintable(QString::number(currentThreadId())));
00584    fflush(stdout);
00585    emit closeSocket(sock);
00586    printf("QcjHttpService::haveTimeOut(%s): Exit\n", qPrintable(QString::number(currentThreadId())));
00587    fflush(stdout);
00588 }
00589 
00590 
00591 
00592 
00593 
00594 
00595 
00596 
00597 

Generated on Fri May 4 11:21:12 2007 for PhotoGrotto by  doxygen 1.5.0