src/network/access/qhttpnetworkreply.cpp
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
     8 **
     8 **
   177 }
   177 }
   178 
   178 
   179 QByteArray QHttpNetworkReply::readAny()
   179 QByteArray QHttpNetworkReply::readAny()
   180 {
   180 {
   181     Q_D(QHttpNetworkReply);
   181     Q_D(QHttpNetworkReply);
       
   182     // we'll take the last buffer, so schedule another read from http
       
   183     if (d->downstreamLimited && d->responseData.bufferCount() == 1)
       
   184         d->connection->d_func()->readMoreLater(this);
   182     return d->responseData.read();
   185     return d->responseData.read();
       
   186 }
       
   187 
       
   188 void QHttpNetworkReply::setDownstreamLimited(bool dsl)
       
   189 {
       
   190     Q_D(QHttpNetworkReply);
       
   191     d->downstreamLimited = dsl;
       
   192     d->connection->d_func()->readMoreLater(this);
   183 }
   193 }
   184 
   194 
   185 bool QHttpNetworkReply::isFinished() const
   195 bool QHttpNetworkReply::isFinished() const
   186 {
   196 {
   187     return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
   197     return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
   199       chunkedTransferEncoding(false),
   209       chunkedTransferEncoding(false),
   200       connectionCloseEnabled(true),
   210       connectionCloseEnabled(true),
   201       forceConnectionCloseEnabled(false),
   211       forceConnectionCloseEnabled(false),
   202       currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
   212       currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
   203       autoDecompress(false), responseData(), requestIsPrepared(false)
   213       autoDecompress(false), responseData(), requestIsPrepared(false)
   204       ,pipeliningUsed(false)
   214       ,pipeliningUsed(false), downstreamLimited(false)
   205 {
   215 {
   206 }
   216 }
   207 
   217 
   208 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
   218 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
   209 {
   219 {
   210 }
   220 }
   211 
   221 
   212 void QHttpNetworkReplyPrivate::clear()
   222 void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
   213 {
   223 {
   214     state = NothingDoneState;
   224     state = NothingDoneState;
   215     statusCode = 100;
   225     statusCode = 100;
   216     bodyLength = 0;
   226     bodyLength = 0;
   217     contentRead = 0;
   227     contentRead = 0;
   218     totalProgress = 0;
   228     totalProgress = 0;
   219     currentChunkSize = 0;
   229     currentChunkSize = 0;
   220     currentChunkRead = 0;
   230     currentChunkRead = 0;
   221     connectionCloseEnabled = true;
   231     connectionCloseEnabled = true;
   222     connection = 0;
       
   223 #ifndef QT_NO_COMPRESS
   232 #ifndef QT_NO_COMPRESS
   224     if (initInflate)
   233     if (initInflate)
   225         inflateEnd(&inflateStrm);
   234         inflateEnd(&inflateStrm);
   226 #endif
   235 #endif
   227     initInflate = false;
   236     initInflate = false;
   228     streamEnd = false;
   237     streamEnd = false;
       
   238     fields.clear();
       
   239 }
       
   240 
       
   241 // TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
       
   242 void QHttpNetworkReplyPrivate::clear()
       
   243 {
       
   244     connection = 0;
       
   245     connectionChannel = 0;
   229     autoDecompress = false;
   246     autoDecompress = false;
   230     fields.clear();
   247     clearHttpLayerInformation();
   231 }
   248 }
   232 
   249 
   233 // QHttpNetworkReplyPrivate
   250 // QHttpNetworkReplyPrivate
   234 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
   251 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
   235 {
   252 {
   411 }
   428 }
   412 #endif
   429 #endif
   413 
   430 
   414 qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
   431 qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
   415 {
   432 {
       
   433     if (fragment.isEmpty()) {
       
   434         // reserve bytes for the status line. This is better than always append() which reallocs the byte array
       
   435         fragment.reserve(32);
       
   436     }
       
   437 
   416     qint64 bytes = 0;
   438     qint64 bytes = 0;
   417     char c;
   439     char c;
   418 
   440     qint64 haveRead = 0;
   419     while (socket->bytesAvailable()) {
   441 
       
   442     do {
       
   443         haveRead = socket->read(&c, 1);
       
   444         if (haveRead == -1)
       
   445             return -1; // unexpected EOF
       
   446         else if (haveRead == 0)
       
   447             break; // read more later
       
   448 
       
   449         bytes++;
       
   450 
   420         // allow both CRLF & LF (only) line endings
   451         // allow both CRLF & LF (only) line endings
   421         if (socket->peek(&c, 1) == 1 && c == '\n') {
   452         if (c == '\n') {
   422             bytes += socket->read(&c, 1); // read the "n"
       
   423             // remove the CR at the end
   453             // remove the CR at the end
   424             if (fragment.endsWith('\r')) {
   454             if (fragment.endsWith('\r')) {
   425                 fragment.truncate(fragment.length()-1);
   455                 fragment.truncate(fragment.length()-1);
   426             }
   456             }
   427             bool ok = parseStatus(fragment);
   457             bool ok = parseStatus(fragment);
   430             if (!ok) {
   460             if (!ok) {
   431                 return -1;
   461                 return -1;
   432             }
   462             }
   433             break;
   463             break;
   434         } else {
   464         } else {
   435             c = 0;
       
   436             int haveRead = socket->read(&c, 1);
       
   437             if (haveRead == -1)
       
   438                 return -1;
       
   439             bytes += haveRead;
       
   440             fragment.append(c);
   465             fragment.append(c);
   441         }
   466         }
   442 
   467 
   443         // is this a valid reply?
   468         // is this a valid reply?
   444         if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
   469         if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
   445         {
   470         {
   446             fragment.clear();
   471             fragment.clear();
   447             return -1;
   472             return -1;
   448         }
   473         }
   449 
   474     } while (haveRead == 1);
   450     }
       
   451 
   475 
   452     return bytes;
   476     return bytes;
   453 }
   477 }
   454 
   478 
   455 bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
   479 bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
   488     return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
   512     return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
   489 }
   513 }
   490 
   514 
   491 qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
   515 qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
   492 {
   516 {
       
   517     if (fragment.isEmpty()) {
       
   518         // according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
       
   519         // block is 381 bytes.
       
   520         // reserve bytes. This is better than always append() which reallocs the byte array.
       
   521         fragment.reserve(512);
       
   522     }
       
   523 
   493     qint64 bytes = 0;
   524     qint64 bytes = 0;
   494     char c = 0;
   525     char c = 0;
   495     bool allHeaders = false;
   526     bool allHeaders = false;
   496     while (!allHeaders && socket->bytesAvailable()) {
   527     qint64 haveRead = 0;
   497         if (socket->peek(&c, 1) == 1 && c == '\n') {
   528     do {
   498             // check for possible header endings. As per HTTP rfc,
   529         haveRead = socket->read(&c, 1);
   499             // the header endings will be marked by CRLFCRLF. But
   530         if (haveRead == 0) {
   500             // we will allow CRLFLF, LFLF & CRLFCRLF
   531             // read more later
   501             if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
   532             break;
   502                 allHeaders = true;
   533         } else if (haveRead == -1) {
   503         }
   534             // connection broke down
   504         bytes += socket->read(&c, 1);
   535             return -1;
   505         fragment.append(c);
   536         } else {
   506     }
   537             fragment.append(c);
       
   538             bytes++;
       
   539 
       
   540             if (c == '\n') {
       
   541                 // check for possible header endings. As per HTTP rfc,
       
   542                 // the header endings will be marked by CRLFCRLF. But
       
   543                 // we will allow CRLFCRLF, CRLFLF, LFLF
       
   544                 if (fragment.endsWith("\r\n\r\n")
       
   545                     || fragment.endsWith("\r\n\n")
       
   546                     || fragment.endsWith("\n\n"))
       
   547                     allHeaders = true;
       
   548 
       
   549                 // there is another case: We have no headers. Then the fragment equals just the line ending
       
   550                 if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
       
   551                     || (fragment.length() == 1 && fragment.endsWith("\n")))
       
   552                     allHeaders = true;
       
   553             }
       
   554         }
       
   555     } while (!allHeaders && haveRead > 0);
       
   556 
   507     // we received all headers now parse them
   557     // we received all headers now parse them
   508     if (allHeaders) {
   558     if (allHeaders) {
   509         parseHeader(fragment);
   559         parseHeader(fragment);
   510         state = ReadingDataState;
   560         state = ReadingDataState;
   511         fragment.clear(); // next fragment
   561         fragment.clear(); // next fragment
   763 #ifndef QT_NO_OPENSSL
   813 #ifndef QT_NO_OPENSSL
   764 
   814 
   765 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
   815 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
   766 {
   816 {
   767     Q_D(const QHttpNetworkReply);
   817     Q_D(const QHttpNetworkReply);
   768     if (d->connection)
   818 
   769         return d->connection->d_func()->sslConfiguration(*this);
   819     if (!d->connectionChannel)
   770     return QSslConfiguration();
   820         return QSslConfiguration();
       
   821 
       
   822     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
       
   823     if (!sslSocket)
       
   824         return QSslConfiguration();
       
   825 
       
   826     return sslSocket->sslConfiguration();
   771 }
   827 }
   772 
   828 
   773 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
   829 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
   774 {
   830 {
   775     Q_D(QHttpNetworkReply);
   831     Q_D(QHttpNetworkReply);