src/qt3support/network/q3http.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the Qt3Support module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include <qplatformdefs.h>
00025 #include "q3http.h"
00026 
00027 #ifndef QT_NO_NETWORKPROTOCOL_HTTP
00028 
00029 #include "q3socket.h"
00030 #include "qtextstream.h"
00031 #include "qmap.h"
00032 #include "qstring.h"
00033 #include "qstringlist.h"
00034 #include "q3cstring.h"
00035 #include "qbuffer.h"
00036 #include "q3urloperator.h"
00037 #include "qtimer.h"
00038 #include "private/q3membuf_p.h"
00039 #include "qevent.h"
00040 #include "q3url.h"
00041 #include "qhttp.h"
00042 
00043 //#define Q3HTTP_DEBUG
00044 
00045 class Q3HttpPrivate
00046 {
00047 public:
00048     Q3HttpPrivate() :
00049   state( Q3Http::Unconnected ),
00050   error( Q3Http::NoError ),
00051   hostname( QString() ),
00052   port( 0 ),
00053   toDevice( 0 ),
00054   postDevice( 0 ),
00055   bytesDone( 0 ),
00056   chunkedSize( -1 ),
00057   idleTimer( 0 )
00058     {
00059   pending.setAutoDelete( true );
00060     }
00061 
00062     Q3Socket socket;
00063     Q3PtrList<Q3HttpRequest> pending;
00064 
00065     Q3Http::State state;
00066     Q3Http::Error error;
00067     QString errorString;
00068 
00069     QString hostname;
00070     Q_UINT16 port;
00071 
00072     QByteArray buffer;
00073     QIODevice* toDevice;
00074     QIODevice* postDevice;
00075 
00076     uint bytesDone;
00077     uint bytesTotal;
00078     Q_LONG chunkedSize;
00079 
00080     Q3HttpRequestHeader header;
00081 
00082     bool readHeader;
00083     QString headerStr;
00084     Q3HttpResponseHeader response;
00085 
00086     int idleTimer;
00087 
00088     Q3Membuf rba;
00089 };
00090 
00091 class Q3HttpRequest
00092 {
00093 public:
00094     Q3HttpRequest()
00095     {
00096   id = ++idCounter;
00097     }
00098     virtual ~Q3HttpRequest()
00099     { }
00100 
00101     virtual void start( Q3Http * ) = 0;
00102     virtual bool hasRequestHeader();
00103     virtual Q3HttpRequestHeader requestHeader();
00104 
00105     virtual QIODevice* sourceDevice() = 0;
00106     virtual QIODevice* destinationDevice() = 0;
00107 
00108     int id;
00109 
00110 private:
00111     static int idCounter;
00112 };
00113 
00114 int Q3HttpRequest::idCounter = 0;
00115 
00116 bool Q3HttpRequest::hasRequestHeader()
00117 {
00118     return false;
00119 }
00120 
00121 Q3HttpRequestHeader Q3HttpRequest::requestHeader()
00122 {
00123     return Q3HttpRequestHeader();
00124 }
00125 
00126 /****************************************************
00127  *
00128  * Q3HttpNormalRequest
00129  *
00130  ****************************************************/
00131 
00132 class Q3HttpNormalRequest : public Q3HttpRequest
00133 {
00134 public:
00135     Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
00136   header(h), to(t)
00137     {
00138   is_ba = false;
00139   data.dev = d;
00140     }
00141 
00142     Q3HttpNormalRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
00143   header(h), to(t)
00144     {
00145   is_ba = true;
00146   data.ba = d;
00147     }
00148 
00149     ~Q3HttpNormalRequest()
00150     {
00151   if ( is_ba )
00152       delete data.ba;
00153     }
00154 
00155     void start( Q3Http * );
00156     bool hasRequestHeader();
00157     Q3HttpRequestHeader requestHeader();
00158 
00159     QIODevice* sourceDevice();
00160     QIODevice* destinationDevice();
00161 
00162 protected:
00163     Q3HttpRequestHeader header;
00164 
00165 private:
00166     union {
00167   QByteArray *ba;
00168   QIODevice *dev;
00169     } data;
00170     bool is_ba;
00171     QIODevice *to;
00172 };
00173 
00174 void Q3HttpNormalRequest::start( Q3Http *http )
00175 {
00176     http->d->header = header;
00177 
00178     if ( is_ba ) {
00179   http->d->buffer = *data.ba;
00180   if ( http->d->buffer.size() > 0 )
00181       http->d->header.setContentLength( http->d->buffer.size() );
00182 
00183   http->d->postDevice = 0;
00184     } else {
00185   http->d->buffer = QByteArray();
00186 
00187   if ( data.dev && ( data.dev->isOpen() || data.dev->open(IO_ReadOnly) ) ) {
00188       http->d->postDevice = data.dev;
00189       if ( http->d->postDevice->size() > 0 )
00190     http->d->header.setContentLength( http->d->postDevice->size() );
00191   } else {
00192       http->d->postDevice = 0;
00193   }
00194     }
00195 
00196     if ( to && ( to->isOpen() || to->open(IO_WriteOnly) ) )
00197   http->d->toDevice = to;
00198     else
00199   http->d->toDevice = 0;
00200 
00201     http->sendRequest();
00202 }
00203 
00204 bool Q3HttpNormalRequest::hasRequestHeader()
00205 {
00206     return true;
00207 }
00208 
00209 Q3HttpRequestHeader Q3HttpNormalRequest::requestHeader()
00210 {
00211     return header;
00212 }
00213 
00214 QIODevice* Q3HttpNormalRequest::sourceDevice()
00215 {
00216     if ( is_ba )
00217   return 0;
00218     return data.dev;
00219 }
00220 
00221 QIODevice* Q3HttpNormalRequest::destinationDevice()
00222 {
00223     return to;
00224 }
00225 
00226 /****************************************************
00227  *
00228  * Q3HttpPGHRequest
00229  * (like a Q3HttpNormalRequest, but for the convenience
00230  * functions put(), get() and head() -- i.e. set the
00231  * host header field correctly before sending the
00232  * request)
00233  *
00234  ****************************************************/
00235 
00236 class Q3HttpPGHRequest : public Q3HttpNormalRequest
00237 {
00238 public:
00239     Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QIODevice *d, QIODevice *t ) :
00240   Q3HttpNormalRequest( h, d, t )
00241     { }
00242 
00243     Q3HttpPGHRequest( const Q3HttpRequestHeader &h, QByteArray *d, QIODevice *t ) :
00244   Q3HttpNormalRequest( h, d, t )
00245     { }
00246 
00247     ~Q3HttpPGHRequest()
00248     { }
00249 
00250     void start( Q3Http * );
00251 };
00252 
00253 void Q3HttpPGHRequest::start( Q3Http *http )
00254 {
00255     header.setValue( "Host", http->d->hostname );
00256     Q3HttpNormalRequest::start( http );
00257 }
00258 
00259 /****************************************************
00260  *
00261  * Q3HttpSetHostRequest
00262  *
00263  ****************************************************/
00264 
00265 class Q3HttpSetHostRequest : public Q3HttpRequest
00266 {
00267 public:
00268     Q3HttpSetHostRequest( const QString &h, Q_UINT16 p ) :
00269   hostname(h), port(p)
00270     { }
00271 
00272     void start( Q3Http * );
00273 
00274     QIODevice* sourceDevice()
00275     { return 0; }
00276     QIODevice* destinationDevice()
00277     { return 0; }
00278 
00279 private:
00280     QString hostname;
00281     Q_UINT16 port;
00282 };
00283 
00284 void Q3HttpSetHostRequest::start( Q3Http *http )
00285 {
00286     http->d->hostname = hostname;
00287     http->d->port = port;
00288     http->finishedWithSuccess();
00289 }
00290 
00291 /****************************************************
00292  *
00293  * Q3HttpCloseRequest
00294  *
00295  ****************************************************/
00296 
00297 class Q3HttpCloseRequest : public Q3HttpRequest
00298 {
00299 public:
00300     Q3HttpCloseRequest()
00301     { }
00302     void start( Q3Http * );
00303 
00304     QIODevice* sourceDevice()
00305     { return 0; }
00306     QIODevice* destinationDevice()
00307     { return 0; }
00308 };
00309 
00310 void Q3HttpCloseRequest::start( Q3Http *http )
00311 {
00312     http->close();
00313 }
00314 
00315 /****************************************************
00316  *
00317  * Q3HttpHeader
00318  *
00319  ****************************************************/
00320 
00374 Q3HttpHeader::Q3HttpHeader()
00375     : valid( true )
00376 {
00377 }
00378 
00382 Q3HttpHeader::Q3HttpHeader( const Q3HttpHeader& header )
00383     : valid( header.valid )
00384 {
00385     values = header.values;
00386 }
00387 
00396 Q3HttpHeader::Q3HttpHeader( const QString& str )
00397     : valid( true )
00398 {
00399     parse( str );
00400 }
00401 
00405 Q3HttpHeader::~Q3HttpHeader()
00406 {
00407 }
00408 
00412 Q3HttpHeader& Q3HttpHeader::operator=( const Q3HttpHeader& h )
00413 {
00414     values = h.values;
00415     valid = h.valid;
00416     return *this;
00417 }
00418 
00424 bool Q3HttpHeader::isValid() const
00425 {
00426     return valid;
00427 }
00428 
00438 bool Q3HttpHeader::parse( const QString& str )
00439 {
00440     QStringList lst;
00441     int pos = str.find( '\n' );
00442     if ( pos > 0 && str.at( pos - 1 ) == '\r' )
00443   lst = QStringList::split( "\r\n", str.stripWhiteSpace(), false );
00444     else
00445   lst = QStringList::split( "\n", str.stripWhiteSpace(), false );
00446 
00447     if ( lst.isEmpty() )
00448   return true;
00449 
00450     QStringList lines;
00451     QStringList::Iterator it = lst.begin();
00452     for( ; it != lst.end(); ++it ) {
00453   if ( !(*it).isEmpty() ) {
00454       if ( (*it)[0].isSpace() ) {
00455     if ( !lines.isEmpty() ) {
00456         lines.last() += " ";
00457         lines.last() += (*it).stripWhiteSpace();
00458     }
00459       } else {
00460     lines.append( (*it) );
00461       }
00462   }
00463     }
00464 
00465     int number = 0;
00466     it = lines.begin();
00467     for( ; it != lines.end(); ++it ) {
00468   if ( !parseLine( *it, number++ ) ) {
00469       valid = false;
00470       return false;
00471   }
00472     }
00473     return true;
00474 }
00475 
00478 void Q3HttpHeader::setValid( bool v )
00479 {
00480     valid = v;
00481 }
00482 
00489 QString Q3HttpHeader::value( const QString& key ) const
00490 {
00491     return values[ key.lower() ];
00492 }
00493 
00499 QStringList Q3HttpHeader::keys() const
00500 {
00501     return values.keys();
00502 }
00503 
00510 bool Q3HttpHeader::hasKey( const QString& key ) const
00511 {
00512     return values.contains( key.lower() );
00513 }
00514 
00525 void Q3HttpHeader::setValue( const QString& key, const QString& value )
00526 {
00527     values[ key.lower() ] = value;
00528 }
00529 
00535 void Q3HttpHeader::removeValue( const QString& key )
00536 {
00537     values.remove( key.lower() );
00538 }
00539 
00548 bool Q3HttpHeader::parseLine( const QString& line, int )
00549 {
00550     int i = line.find( ":" );
00551     if ( i == -1 )
00552   return false;
00553 
00554     values.insert( line.left( i ).stripWhiteSpace().lower(), line.mid( i + 1 ).stripWhiteSpace() );
00555 
00556     return true;
00557 }
00558 
00566 QString Q3HttpHeader::toString() const
00567 {
00568     if ( !isValid() )
00569   return "";
00570 
00571     QString ret = "";
00572 
00573     QMap<QString,QString>::ConstIterator it = values.begin();
00574     for( ; it != values.end(); ++it )
00575   ret += it.key() + ": " + it.data() + "\r\n";
00576 
00577     return ret;
00578 }
00579 
00586 bool Q3HttpHeader::hasContentLength() const
00587 {
00588     return hasKey( "content-length" );
00589 }
00590 
00597 uint Q3HttpHeader::contentLength() const
00598 {
00599     return values[ "content-length" ].toUInt();
00600 }
00601 
00608 void Q3HttpHeader::setContentLength( int len )
00609 {
00610     values[ "content-length" ] = QString::number( len );
00611 }
00612 
00619 bool Q3HttpHeader::hasContentType() const
00620 {
00621     return hasKey( "content-type" );
00622 }
00623 
00629 QString Q3HttpHeader::contentType() const
00630 {
00631     QString type = values[ "content-type" ];
00632     if ( type.isEmpty() )
00633   return QString();
00634 
00635     int pos = type.find( ";" );
00636     if ( pos == -1 )
00637   return type;
00638 
00639     return type.left( pos ).stripWhiteSpace();
00640 }
00641 
00648 void Q3HttpHeader::setContentType( const QString& type )
00649 {
00650     values[ "content-type" ] = type;
00651 }
00652 
00653 /****************************************************
00654  *
00655  * Q3HttpResponseHeader
00656  *
00657  ****************************************************/
00658 
00681 Q3HttpResponseHeader::Q3HttpResponseHeader()
00682 {
00683     setValid( false );
00684 }
00685 
00691 Q3HttpResponseHeader::Q3HttpResponseHeader( int code, const QString& text, int majorVer, int minorVer )
00692     : Q3HttpHeader(), statCode( code ), reasonPhr( text ), majVer( majorVer ), minVer( minorVer )
00693 {
00694 }
00695 
00699 Q3HttpResponseHeader::Q3HttpResponseHeader( const Q3HttpResponseHeader& header )
00700     : Q3HttpHeader( header ), statCode( header.statCode ), reasonPhr( header.reasonPhr ), majVer( header.majVer ), minVer( header.minVer )
00701 {
00702 }
00703 
00712 Q3HttpResponseHeader::Q3HttpResponseHeader( const QString& str )
00713     : Q3HttpHeader()
00714 {
00715     parse( str );
00716 }
00717 
00724 void Q3HttpResponseHeader::setStatusLine( int code, const QString& text, int majorVer, int minorVer )
00725 {
00726     setValid( true );
00727     statCode = code;
00728     reasonPhr = text;
00729     majVer = majorVer;
00730     minVer = minorVer;
00731 }
00732 
00738 int Q3HttpResponseHeader::statusCode() const
00739 {
00740     return statCode;
00741 }
00742 
00748 QString Q3HttpResponseHeader::reasonPhrase() const
00749 {
00750     return reasonPhr;
00751 }
00752 
00758 int Q3HttpResponseHeader::majorVersion() const
00759 {
00760     return majVer;
00761 }
00762 
00768 int Q3HttpResponseHeader::minorVersion() const
00769 {
00770     return minVer;
00771 }
00772 
00775 bool Q3HttpResponseHeader::parseLine( const QString& line, int number )
00776 {
00777     if ( number != 0 )
00778   return Q3HttpHeader::parseLine( line, number );
00779 
00780     QString l = line.simplifyWhiteSpace();
00781     if ( l.length() < 10 )
00782   return false;
00783 
00784     if ( l.left( 5 ) == "HTTP/" && l[5].isDigit() && l[6] == '.' &&
00785       l[7].isDigit() && l[8] == ' ' && l[9].isDigit() ) {
00786   majVer = l[5].latin1() - '0';
00787   minVer = l[7].latin1() - '0';
00788 
00789   int pos = l.find( ' ', 9 );
00790   if ( pos != -1 ) {
00791       reasonPhr = l.mid( pos + 1 );
00792       statCode = l.mid( 9, pos - 9 ).toInt();
00793   } else {
00794       statCode = l.mid( 9 ).toInt();
00795       reasonPhr.clear();
00796   }
00797     } else {
00798   return false;
00799     }
00800 
00801     return true;
00802 }
00803 
00806 QString Q3HttpResponseHeader::toString() const
00807 {
00808     QString ret( "HTTP/%1.%2 %3 %4\r\n%5\r\n" );
00809     return ret.arg( majVer ).arg ( minVer ).arg( statCode ).arg( reasonPhr ).arg( Q3HttpHeader::toString() );
00810 }
00811 
00812 /****************************************************
00813  *
00814  * Q3HttpRequestHeader
00815  *
00816  ****************************************************/
00817 
00848 Q3HttpRequestHeader::Q3HttpRequestHeader()
00849     : Q3HttpHeader()
00850 {
00851     setValid( false );
00852 }
00853 
00858 Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& method, const QString& path, int majorVer, int minorVer )
00859     : Q3HttpHeader(), m( method ), p( path ), majVer( majorVer ), minVer( minorVer )
00860 {
00861 }
00862 
00866 Q3HttpRequestHeader::Q3HttpRequestHeader( const Q3HttpRequestHeader& header )
00867     : Q3HttpHeader( header ), m( header.m ), p( header.p ), majVer( header.majVer ), minVer( header.minVer )
00868 {
00869 }
00870 
00878 Q3HttpRequestHeader::Q3HttpRequestHeader( const QString& str )
00879     : Q3HttpHeader()
00880 {
00881     parse( str );
00882 }
00883 
00891 void Q3HttpRequestHeader::setRequest( const QString& method, const QString& path, int majorVer, int minorVer )
00892 {
00893     setValid( true );
00894     m = method;
00895     p = path;
00896     majVer = majorVer;
00897     minVer = minorVer;
00898 }
00899 
00905 QString Q3HttpRequestHeader::method() const
00906 {
00907     return m;
00908 }
00909 
00915 QString Q3HttpRequestHeader::path() const
00916 {
00917     return p;
00918 }
00919 
00925 int Q3HttpRequestHeader::majorVersion() const
00926 {
00927     return majVer;
00928 }
00929 
00935 int Q3HttpRequestHeader::minorVersion() const
00936 {
00937     return minVer;
00938 }
00939 
00942 bool Q3HttpRequestHeader::parseLine( const QString& line, int number )
00943 {
00944     if ( number != 0 )
00945   return Q3HttpHeader::parseLine( line, number );
00946 
00947     QStringList lst = QStringList::split( " ", line.simplifyWhiteSpace() );
00948     if ( lst.count() > 0 ) {
00949   m = lst[0];
00950   if ( lst.count() > 1 ) {
00951       p = lst[1];
00952       if ( lst.count() > 2 ) {
00953     QString v = lst[2];
00954     if ( v.length() >= 8 && v.left( 5 ) == "HTTP/" &&
00955       v[5].isDigit() && v[6] == '.' && v[7].isDigit() ) {
00956         majVer = v[5].latin1() - '0';
00957         minVer = v[7].latin1() - '0';
00958         return true;
00959     }
00960       }
00961   }
00962     }
00963 
00964     return false;
00965 }
00966 
00969 QString Q3HttpRequestHeader::toString() const
00970 {
00971     QString first( "%1 %2");
00972     QString last(" HTTP/%3.%4\r\n%5\r\n" );
00973     return first.arg( m ).arg( p ) +
00974   last.arg( majVer ).arg( minVer ).arg( Q3HttpHeader::toString());
00975 }
00976 
00977 
00978 /****************************************************
00979  *
00980  * Q3Http
00981  *
00982  ****************************************************/
01152 Q3Http::Q3Http()
01153 {
01154     init();
01155 }
01156 
01161 Q3Http::Q3Http( QObject* parent, const char* name )
01162 {
01163     if ( parent )
01164   parent->insertChild( this );
01165     setName( name );
01166     init();
01167 }
01168 
01177 Q3Http::Q3Http( const QString &hostname, Q_UINT16 port, QObject* parent, const char* name )
01178 {
01179     if ( parent )
01180   parent->insertChild( this );
01181     setName( name );
01182     init();
01183 
01184     d->hostname = hostname;
01185     d->port = port;
01186 }
01187 
01188 void Q3Http::init()
01189 {
01190     bytesRead = 0;
01191     d = new Q3HttpPrivate;
01192     d->errorString = QHttp::tr( "Unknown error" );
01193 
01194     connect( &d->socket, SIGNAL(connected()),
01195       this, SLOT(slotConnected()) );
01196     connect( &d->socket, SIGNAL(connectionClosed()),
01197       this, SLOT(slotClosed()) );
01198     connect( &d->socket, SIGNAL(delayedCloseFinished()),
01199       this, SLOT(slotClosed()) );
01200     connect( &d->socket, SIGNAL(readyRead()),
01201       this, SLOT(slotReadyRead()) );
01202     connect( &d->socket, SIGNAL(error(int)),
01203       this, SLOT(slotError(int)) );
01204     connect( &d->socket, SIGNAL(bytesWritten(int)),
01205       this, SLOT(slotBytesWritten(int)) );
01206 
01207     d->idleTimer = startTimer( 0 );
01208 }
01209 
01214 Q3Http::~Q3Http()
01215 {
01216     abort();
01217     delete d;
01218 }
01219 
01383 void Q3Http::abort()
01384 {
01385     Q3HttpRequest *r = d->pending.getFirst();
01386     if ( r == 0 )
01387   return;
01388 
01389     finishedWithError( QHttp::tr("Request aborted"), Aborted );
01390     clearPendingRequests();
01391     d->socket.clearPendingData();
01392     close();
01393 }
01394 
01401 Q_ULONG Q3Http::bytesAvailable() const
01402 {
01403 #if defined(Q3HTTP_DEBUG)
01404     qDebug( "Q3Http::bytesAvailable(): %d bytes", (int)d->rba.size() );
01405 #endif
01406     return d->rba.size();
01407 }
01408 
01415 Q_LONG Q3Http::readBlock( char *data, Q_ULONG maxlen )
01416 {
01417     if ( data == 0 && maxlen != 0 ) {
01418 #if defined(QT_CHECK_NULL)
01419   qWarning( "Q3Http::readBlock: Null pointer error" );
01420 #endif
01421   return -1;
01422     }
01423     if ( maxlen >= (Q_ULONG)d->rba.size() )
01424   maxlen = d->rba.size();
01425     d->rba.consumeBytes( maxlen, data );
01426 
01427     d->bytesDone += maxlen;
01428 #if defined(Q3HTTP_DEBUG)
01429     qDebug( "Q3Http::readBlock(): read %d bytes (%d bytes done)", (int)maxlen, d->bytesDone );
01430 #endif
01431     return maxlen;
01432 }
01433 
01439 QByteArray Q3Http::readAll()
01440 {
01441     Q_ULONG avail = bytesAvailable();
01442     QByteArray tmp( avail );
01443     Q_LONG read = readBlock( tmp.data(), avail );
01444     tmp.resize( read );
01445     return tmp;
01446 }
01447 
01454 int Q3Http::currentId() const
01455 {
01456     Q3HttpRequest *r = d->pending.getFirst();
01457     if ( r == 0 )
01458   return 0;
01459     return r->id;
01460 }
01461 
01470 Q3HttpRequestHeader Q3Http::currentRequest() const
01471 {
01472     Q3HttpRequest *r = d->pending.getFirst();
01473     if ( r != 0 && r->hasRequestHeader() )
01474   return r->requestHeader();
01475     return Q3HttpRequestHeader();
01476 }
01477 
01488 QIODevice* Q3Http::currentSourceDevice() const
01489 {
01490     Q3HttpRequest *r = d->pending.getFirst();
01491     if ( !r )
01492   return 0;
01493     return r->sourceDevice();
01494 }
01495 
01506 QIODevice* Q3Http::currentDestinationDevice() const
01507 {
01508     Q3HttpRequest *r = d->pending.getFirst();
01509     if ( !r )
01510   return 0;
01511     return r->destinationDevice();
01512 }
01513 
01523 bool Q3Http::hasPendingRequests() const
01524 {
01525     return d->pending.count() > 1;
01526 }
01527 
01535 void Q3Http::clearPendingRequests()
01536 {
01537     Q3HttpRequest *r = 0;
01538     if ( d->pending.count() > 0 )
01539   r = d->pending.take( 0 );
01540     d->pending.clear();
01541     if ( r )
01542   d->pending.append( r );
01543 }
01544 
01560 int Q3Http::setHost(const QString &hostname, Q_UINT16 port )
01561 {
01562     return addRequest( new Q3HttpSetHostRequest( hostname, port ) );
01563 }
01564 
01591 int Q3Http::get( const QString& path, QIODevice* to )
01592 {
01593     Q3HttpRequestHeader header( "GET", path );
01594     header.setValue( "Connection", "Keep-Alive" );
01595     return addRequest( new Q3HttpPGHRequest( header, (QIODevice*)0, to ) );
01596 }
01597 
01626 int Q3Http::post( const QString& path, QIODevice* data, QIODevice* to  )
01627 {
01628     Q3HttpRequestHeader header( "POST", path );
01629     header.setValue( "Connection", "Keep-Alive" );
01630     return addRequest( new Q3HttpPGHRequest( header, data, to ) );
01631 }
01632 
01638 int Q3Http::post( const QString& path, const QByteArray& data, QIODevice* to )
01639 {
01640     Q3HttpRequestHeader header( "POST", path );
01641     header.setValue( "Connection", "Keep-Alive" );
01642     return addRequest( new Q3HttpPGHRequest( header, new QByteArray(data), to ) );
01643 }
01644 
01663 int Q3Http::head( const QString& path )
01664 {
01665     Q3HttpRequestHeader header( "HEAD", path );
01666     header.setValue( "Connection", "Keep-Alive" );
01667     return addRequest( new Q3HttpPGHRequest( header, (QIODevice*)0, 0 ) );
01668 }
01669 
01697 int Q3Http::request( const Q3HttpRequestHeader &header, QIODevice *data, QIODevice *to )
01698 {
01699     return addRequest( new Q3HttpNormalRequest( header, data, to ) );
01700 }
01701 
01707 int Q3Http::request( const Q3HttpRequestHeader &header, const QByteArray &data, QIODevice *to  )
01708 {
01709     return addRequest( new Q3HttpNormalRequest( header, new QByteArray(data), to ) );
01710 }
01711 
01736 int Q3Http::closeConnection()
01737 {
01738     return addRequest( new Q3HttpCloseRequest() );
01739 }
01740 
01741 int Q3Http::addRequest( Q3HttpRequest *req )
01742 {
01743     d->pending.append( req );
01744 
01745     if ( d->pending.count() == 1 )
01746   // don't emit the requestStarted() signal before the id is returned
01747   QTimer::singleShot( 0, this, SLOT(startNextRequest()) );
01748 
01749     return req->id;
01750 }
01751 
01752 void Q3Http::startNextRequest()
01753 {
01754     Q3HttpRequest *r = d->pending.getFirst();
01755     if ( r == 0 )
01756   return;
01757 
01758     d->error = NoError;
01759     d->errorString = QHttp::tr( "Unknown error" );
01760 
01761     if ( bytesAvailable() )
01762   readAll(); // clear the data
01763     emit requestStarted( r->id );
01764     r->start( this );
01765 }
01766 
01767 void Q3Http::sendRequest()
01768 {
01769     if ( d->hostname.isNull() ) {
01770   finishedWithError( QHttp::tr("No server set to connect to"), UnknownError );
01771   return;
01772     }
01773 
01774     killIdleTimer();
01775 
01776     // Do we need to setup a new connection or can we reuse an
01777     // existing one ?
01778     if ( d->socket.peerName() != d->hostname || d->socket.peerPort() != d->port
01779         || d->socket.state() != Q3Socket::Connection ) {
01780   setState( Q3Http::Connecting );
01781   d->socket.connectToHost( d->hostname, d->port );
01782     } else {
01783         slotConnected();
01784     }
01785 
01786 }
01787 
01788 void Q3Http::finishedWithSuccess()
01789 {
01790     Q3HttpRequest *r = d->pending.getFirst();
01791     if ( r == 0 )
01792   return;
01793 
01794     emit requestFinished( r->id, false );
01795     d->pending.removeFirst();
01796     if ( d->pending.isEmpty() ) {
01797   emit done( false );
01798     } else {
01799   startNextRequest();
01800     }
01801 }
01802 
01803 void Q3Http::finishedWithError( const QString& detail, int errorCode )
01804 {
01805     Q3HttpRequest *r = d->pending.getFirst();
01806     if ( r == 0 )
01807   return;
01808 
01809     d->error = (Error)errorCode;
01810     d->errorString = detail;
01811     emit requestFinished( r->id, true );
01812 
01813     d->pending.clear();
01814     emit done( true );
01815 }
01816 
01817 void Q3Http::slotClosed()
01818 {
01819     if ( d->state == Closing )
01820   return;
01821 
01822     if ( d->state == Reading ) {
01823   if ( d->response.hasKey( "content-length" ) ) {
01824       // We got Content-Length, so did we get all bytes?
01825       if ( d->bytesDone+bytesAvailable() != d->response.contentLength() ) {
01826     finishedWithError( QHttp::tr("Wrong content length"), WrongContentLength );
01827       }
01828   }
01829     } else if ( d->state == Connecting || d->state == Sending ) {
01830   finishedWithError( QHttp::tr("Server closed connection unexpectedly"), UnexpectedClose );
01831     }
01832 
01833     d->postDevice = 0;
01834     setState( Closing );
01835     d->idleTimer = startTimer( 0 );
01836 }
01837 
01838 void Q3Http::slotConnected()
01839 {
01840     if ( d->state != Sending ) {
01841   d->bytesDone = 0;
01842   setState( Sending );
01843     }
01844 
01845     QString str = d->header.toString();
01846     d->bytesTotal = str.length();
01847     d->socket.writeBlock( str.latin1(), d->bytesTotal );
01848 #if defined(Q3HTTP_DEBUG)
01849     qDebug( "Q3Http: write request header:\n---{\n%s}---", str.latin1() );
01850 #endif
01851 
01852     if ( d->postDevice ) {
01853   d->bytesTotal += d->postDevice->size();
01854     } else {
01855   d->bytesTotal += d->buffer.size();
01856   d->socket.writeBlock( d->buffer.data(), d->buffer.size() );
01857   d->buffer = QByteArray(); // save memory
01858     }
01859 }
01860 
01861 void Q3Http::slotError( int err )
01862 {
01863     d->postDevice = 0;
01864 
01865     if ( d->state == Connecting || d->state == Reading || d->state == Sending ) {
01866   switch ( err ) {
01867       case Q3Socket::ErrConnectionRefused:
01868     finishedWithError( QHttp::tr("Connection refused"), ConnectionRefused );
01869     break;
01870       case Q3Socket::ErrHostNotFound:
01871     finishedWithError( QHttp::tr("Host %1 not found").arg(d->socket.peerName()), HostNotFound );
01872     break;
01873       default:
01874     finishedWithError( QHttp::tr("HTTP request failed"), UnknownError );
01875     break;
01876   }
01877     }
01878 
01879     close();
01880 }
01881 
01882 void Q3Http::slotBytesWritten( int written )
01883 {
01884     d->bytesDone += written;
01885     emit dataSendProgress( d->bytesDone, d->bytesTotal );
01886 
01887     if ( !d->postDevice )
01888   return;
01889 
01890     if ( d->socket.bytesToWrite() == 0 ) {
01891   int max = qMin<int>( 4096, d->postDevice->size() - d->postDevice->at() );
01892   QByteArray arr( max );
01893 
01894   int n = d->postDevice->readBlock( arr.data(), max );
01895   if ( n != max ) {
01896       qWarning("Could not read enough bytes from the device");
01897       close();
01898       return;
01899   }
01900   if ( d->postDevice->atEnd() ) {
01901       d->postDevice = 0;
01902   }
01903 
01904   d->socket.writeBlock( arr.data(), max );
01905     }
01906 }
01907 
01908 void Q3Http::slotReadyRead()
01909 {
01910     if ( d->state != Reading ) {
01911   setState( Reading );
01912   d->buffer = QByteArray();
01913   d->readHeader = true;
01914   d->headerStr = "";
01915   d->bytesDone = 0;
01916   d->chunkedSize = -1;
01917     }
01918 
01919     while ( d->readHeader ) {
01920   bool end = false;
01921   QString tmp;
01922   while ( !end && d->socket.canReadLine() ) {
01923       tmp = d->socket.readLine();
01924       if ( tmp == "\r\n" || tmp == "\n" )
01925     end = true;
01926       else
01927     d->headerStr += tmp;
01928   }
01929 
01930   if ( !end )
01931       return;
01932 
01933 #if defined(Q3HTTP_DEBUG)
01934   qDebug( "Q3Http: read response header:\n---{\n%s}---", d->headerStr.latin1() );
01935 #endif
01936   d->response = Q3HttpResponseHeader( d->headerStr );
01937   d->headerStr = "";
01938 #if defined(Q3HTTP_DEBUG)
01939   qDebug( "Q3Http: read response header:\n---{\n%s}---", d->response.toString().latin1() );
01940 #endif
01941   // Check header
01942   if ( !d->response.isValid() ) {
01943       finishedWithError( QHttp::tr("Invalid HTTP response header"), InvalidResponseHeader );
01944       close();
01945       return;
01946   }
01947 
01948   // The 100-continue header is ignored, because when using the
01949   // POST method, we send both the request header and data in
01950   // one chunk.
01951   if (d->response.statusCode() != 100) {
01952       d->readHeader = false;
01953       if ( d->response.hasKey( "transfer-encoding" ) &&
01954      d->response.value( "transfer-encoding" ).lower().contains( "chunked" ) )
01955     d->chunkedSize = 0;
01956 
01957       emit responseHeaderReceived( d->response );
01958   }
01959     }
01960 
01961     if ( !d->readHeader ) {
01962   bool everythingRead = false;
01963 
01964   if ( currentRequest().method() == "HEAD" ) {
01965       everythingRead = true;
01966   } else {
01967       Q_ULONG n = d->socket.bytesAvailable();
01968       QByteArray *arr = 0;
01969       if ( d->chunkedSize != -1 ) {
01970     // transfer-encoding is chunked
01971     for ( ;; ) {
01972         // get chunk size
01973         if ( d->chunkedSize == 0 ) {
01974       if ( !d->socket.canReadLine() )
01975           break;
01976       QString sizeString = d->socket.readLine();
01977       int tPos = sizeString.find( ';' );
01978       if ( tPos != -1 )
01979           sizeString.truncate( tPos );
01980       bool ok;
01981       d->chunkedSize = sizeString.toInt( &ok, 16 );
01982       if ( !ok ) {
01983           finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
01984           close();
01985                             delete arr;
01986           return;
01987       }
01988       if ( d->chunkedSize == 0 ) // last-chunk
01989           d->chunkedSize = -2;
01990         }
01991 
01992         // read trailer
01993         while ( d->chunkedSize == -2 && d->socket.canReadLine() ) {
01994       QString read = d->socket.readLine();
01995       if ( read == "\r\n" || read == "\n" )
01996           d->chunkedSize = -1;
01997         }
01998         if ( d->chunkedSize == -1 ) {
01999       everythingRead = true;
02000       break;
02001         }
02002 
02003         // make sure that you can read the terminating CRLF,
02004         // otherwise wait until next time...
02005         n = d->socket.bytesAvailable();
02006         if ( n == 0 )
02007       break;
02008         if ( (Q_LONG)n == d->chunkedSize || (Q_LONG)n == d->chunkedSize+1 ) {
02009       n = d->chunkedSize - 1;
02010       if ( n == 0 )
02011           break;
02012         }
02013 
02014         // read data
02015         uint toRead = QMIN( (Q_LONG)n, (d->chunkedSize < 0 ? (Q_LONG)n : d->chunkedSize) );
02016         if ( !arr )
02017       arr = new QByteArray( 0 );
02018         uint oldArrSize = arr->size();
02019         arr->resize( oldArrSize + toRead );
02020         Q_LONG read = d->socket.readBlock( arr->data()+oldArrSize, toRead );
02021         arr->resize( oldArrSize + read );
02022 
02023         d->chunkedSize -= read;
02024 
02025         if ( d->chunkedSize == 0 && n - read >= 2 ) {
02026       // read terminating CRLF
02027       char tmp[2];
02028       d->socket.readBlock( tmp, 2 );
02029       if ( tmp[0] != '\r' || tmp[1] != '\n' ) {
02030           finishedWithError( QHttp::tr("Invalid HTTP chunked body"), WrongContentLength );
02031           close();
02032                             delete arr;
02033           return;
02034       }
02035         }
02036     }
02037       } else if ( d->response.hasContentLength() ) {
02038     n = qMin<ulong>( d->response.contentLength() - d->bytesDone, n );
02039     if ( n > 0 ) {
02040         arr = new QByteArray( n );
02041         Q_LONG read = d->socket.readBlock( arr->data(), n );
02042         arr->resize( read );
02043     }
02044     if ( d->bytesDone + bytesAvailable() + n == d->response.contentLength() )
02045         everythingRead = true;
02046       } else if ( n > 0 ) {
02047     // workaround for VC++ bug
02048     QByteArray temp = d->socket.readAll();
02049     arr = new QByteArray( temp );
02050       }
02051 
02052       if ( arr ) {
02053     n = arr->size();
02054     if ( d->toDevice ) {
02055         d->toDevice->writeBlock( arr->data(), n );
02056         delete arr;
02057         d->bytesDone += n;
02058 #if defined(Q3HTTP_DEBUG)
02059         qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%d bytes done)", n, d->bytesDone );
02060 #endif
02061         if ( d->response.hasContentLength() )
02062       emit dataReadProgress( d->bytesDone, d->response.contentLength() );
02063         else
02064       emit dataReadProgress( d->bytesDone, 0 );
02065     } else {
02066         d->rba.append( arr );
02067 #if defined(Q3HTTP_DEBUG)
02068         qDebug( "Q3Http::slotReadyRead(): read %ld bytes (%ld bytes done)", n, d->bytesDone + bytesAvailable() );
02069 #endif
02070         if ( d->response.hasContentLength() )
02071       emit dataReadProgress( d->bytesDone + bytesAvailable(), d->response.contentLength() );
02072         else
02073       emit dataReadProgress( d->bytesDone + bytesAvailable(), 0 );
02074         emit readyRead( d->response );
02075     }
02076       }
02077   }
02078 
02079   if ( everythingRead ) {
02080       // Handle "Connection: close"
02081       if ( d->response.value("connection").lower() == "close" ) {
02082     close();
02083       } else {
02084     setState( Connected );
02085     // Start a timer, so that we emit the keep alive signal
02086     // "after" this method returned.
02087     d->idleTimer = startTimer( 0 );
02088       }
02089   }
02090     }
02091 }
02092 
02099 Q3Http::State Q3Http::state() const
02100 {
02101     return d->state;
02102 }
02103 
02111 Q3Http::Error Q3Http::error() const
02112 {
02113     return d->error;
02114 }
02115 
02122 QString Q3Http::errorString() const
02123 {
02124     return d->errorString;
02125 }
02126 
02129 void Q3Http::timerEvent( QTimerEvent *e )
02130 {
02131     if ( e->timerId() == d->idleTimer ) {
02132   killTimer( d->idleTimer );
02133   d->idleTimer = 0;
02134 
02135   if ( d->state == Connected ) {
02136       finishedWithSuccess();
02137   } else if ( d->state != Unconnected ) {
02138       setState( Unconnected );
02139       finishedWithSuccess();
02140   }
02141     } else {
02142   QObject::timerEvent( e );
02143     }
02144 }
02145 
02146 void Q3Http::killIdleTimer()
02147 {
02148     if (d->idleTimer)
02149         killTimer( d->idleTimer );
02150     d->idleTimer = 0;
02151 }
02152 
02153 void Q3Http::setState( int s )
02154 {
02155 #if defined(Q3HTTP_DEBUG)
02156     qDebug( "Q3Http state changed %d -> %d", d->state, s );
02157 #endif
02158     d->state = (State)s;
02159     emit stateChanged( s );
02160 }
02161 
02162 void Q3Http::close()
02163 {
02164     // If no connection is open -> ignore
02165     if ( d->state == Closing || d->state == Unconnected )
02166   return;
02167 
02168     d->postDevice = 0;
02169     setState( Closing );
02170 
02171     // Already closed ?
02172     if ( !d->socket.isOpen() ) {
02173   d->idleTimer = startTimer( 0 );
02174     } else {
02175   // Close now.
02176   d->socket.close();
02177 
02178   // Did close succeed immediately ?
02179   if ( d->socket.state() == Q3Socket::Idle ) {
02180       // Prepare to emit the requestFinished() signal.
02181       d->idleTimer = startTimer( 0 );
02182   }
02183     }
02184 }
02185 
02186 /**********************************************************************
02187  *
02188  * Q3Http implementation of the Q3NetworkProtocol interface
02189  *
02190  *********************************************************************/
02193 int Q3Http::supportedOperations() const
02194 {
02195     return OpGet | OpPut;
02196 }
02197 
02200 void Q3Http::operationGet( Q3NetworkOperation *op )
02201 {
02202     connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
02203       this, SLOT(clientReply(Q3HttpResponseHeader)) );
02204     connect( this, SIGNAL(done(bool)),
02205       this, SLOT(clientDone(bool)) );
02206     connect( this, SIGNAL(stateChanged(int)),
02207       this, SLOT(clientStateChanged(int)) );
02208 
02209     bytesRead = 0;
02210     op->setState( StInProgress );
02211     Q3Url u( operationInProgress()->arg( 0 ) );
02212     Q3HttpRequestHeader header( "GET", u.encodedPathAndQuery(), 1, 0 );
02213     header.setValue( "Host", u.host() );
02214     setHost( u.host(), u.port() != -1 ? u.port() : 80 );
02215     request( header );
02216 }
02217 
02220 void Q3Http::operationPut( Q3NetworkOperation *op )
02221 {
02222     connect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
02223       this, SLOT(clientReply(Q3HttpResponseHeader)) );
02224     connect( this, SIGNAL(done(bool)),
02225       this, SLOT(clientDone(bool)) );
02226     connect( this, SIGNAL(stateChanged(int)),
02227       this, SLOT(clientStateChanged(int)) );
02228 
02229     bytesRead = 0;
02230     op->setState( StInProgress );
02231     Q3Url u( operationInProgress()->arg( 0 ) );
02232     Q3HttpRequestHeader header( "POST", u.encodedPathAndQuery(), 1, 0 );
02233     header.setValue( "Host", u.host() );
02234     setHost( u.host(), u.port() != -1 ? u.port() : 80 );
02235     request( header, op->rawArg(1) );
02236 }
02237 
02238 void Q3Http::clientReply( const Q3HttpResponseHeader &rep )
02239 {
02240     Q3NetworkOperation *op = operationInProgress();
02241     if ( op ) {
02242   if ( rep.statusCode() >= 400 && rep.statusCode() < 600 ) {
02243       op->setState( StFailed );
02244       op->setProtocolDetail(
02245         QString("%1 %2").arg(rep.statusCode()).arg(rep.reasonPhrase())
02246                 );
02247       switch ( rep.statusCode() ) {
02248     case 401:
02249     case 403:
02250     case 405:
02251         op->setErrorCode( ErrPermissionDenied );
02252         break;
02253     case 404:
02254         op->setErrorCode(ErrFileNotExisting );
02255         break;
02256     default:
02257         if ( op->operation() == OpGet )
02258       op->setErrorCode( ErrGet );
02259         else
02260       op->setErrorCode( ErrPut );
02261         break;
02262       }
02263   }
02264   // ### In cases of an error, should we still emit the data() signals?
02265   if ( op->operation() == OpGet && bytesAvailable() > 0 ) {
02266       QByteArray ba = readAll();
02267       emit data( ba, op );
02268       bytesRead += ba.size();
02269       if ( rep.hasContentLength() ) {
02270     emit dataTransferProgress( bytesRead, rep.contentLength(), op );
02271       }
02272   }
02273     }
02274 }
02275 
02276 void Q3Http::clientDone( bool err )
02277 {
02278     disconnect( this, SIGNAL(readyRead(Q3HttpResponseHeader)),
02279       this, SLOT(clientReply(Q3HttpResponseHeader)) );
02280     disconnect( this, SIGNAL(done(bool)),
02281       this, SLOT(clientDone(bool)) );
02282     disconnect( this, SIGNAL(stateChanged(int)),
02283       this, SLOT(clientStateChanged(int)) );
02284 
02285     if ( err ) {
02286   Q3NetworkOperation *op = operationInProgress();
02287   if ( op ) {
02288       op->setState( Q3NetworkProtocol::StFailed );
02289       op->setProtocolDetail( errorString() );
02290       switch ( error() ) {
02291     case ConnectionRefused:
02292         op->setErrorCode( ErrHostNotFound );
02293         break;
02294     case HostNotFound:
02295         op->setErrorCode( ErrHostNotFound );
02296         break;
02297     default:
02298         if ( op->operation() == OpGet )
02299       op->setErrorCode( ErrGet );
02300         else
02301       op->setErrorCode( ErrPut );
02302         break;
02303       }
02304       emit finished( op );
02305   }
02306     } else {
02307   Q3NetworkOperation *op = operationInProgress();
02308   if ( op ) {
02309       if ( op->state() != StFailed ) {
02310     op->setState( Q3NetworkProtocol::StDone );
02311     op->setErrorCode( Q3NetworkProtocol::NoError );
02312       }
02313       emit finished( op );
02314   }
02315     }
02316 
02317 }
02318 
02319 void Q3Http::clientStateChanged( int state )
02320 {
02321     if ( url() ) {
02322   switch ( (State)state ) {
02323       case Connecting:
02324     emit connectionStateChanged( ConHostFound, QHttp::tr( "Host %1 found" ).arg( url()->host() ) );
02325     break;
02326       case Sending:
02327     emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host %1" ).arg( url()->host() ) );
02328     break;
02329       case Unconnected:
02330     emit connectionStateChanged( ConClosed, QHttp::tr( "Connection to %1 closed" ).arg( url()->host() ) );
02331     break;
02332       default:
02333     break;
02334   }
02335     } else {
02336   switch ( (State)state ) {
02337       case Connecting:
02338     emit connectionStateChanged( ConHostFound, QHttp::tr( "Host found" ) );
02339     break;
02340       case Sending:
02341     emit connectionStateChanged( ConConnected, QHttp::tr( "Connected to host" ) );
02342     break;
02343       case Unconnected:
02344     emit connectionStateChanged( ConClosed, QHttp::tr( "Connection closed" ) );
02345     break;
02346       default:
02347     break;
02348   }
02349     }
02350 }
02351 
02352 #endif

Generated on Thu Mar 15 11:58:17 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1