diff -r d0e4e85d1805 qhttpnetworkconnectionchannel.cpp --- a/qhttpnetworkconnectionchannel.cpp Sat Dec 22 19:07:01 2012 +0800 +++ b/qhttpnetworkconnectionchannel.cpp Sat Dec 22 19:14:27 2012 +0800 @@ -370,6 +370,7 @@ // read loop for the response qint64 bytes = 0; qint64 lastBytes = bytes; + bool needIgnoreReadingData = false; do { lastBytes = bytes; @@ -380,6 +381,8 @@ // fallthrough } case QHttpNetworkReplyPrivate::ReadingStatusState: { + if( needIgnoreReadingData ) + needIgnoreReadingData = false;//reset; qint64 statusBytes = reply->d_func()->readStatus(socket); if (statusBytes == -1) { // connection broke while reading status. also handled if later _q_disconnected is called @@ -408,11 +411,26 @@ replyPrivate->autoDecompress = false; } if (replyPrivate->statusCode == 100) { + //100 Continue may has body part, as CYBUG-410 case does: + //if 100 Continue's header "Content-Length" is not set, do skill lines which is not startsWith HTTP/; + if( replyPrivate->bodyLength > 0 ){ + needIgnoreReadingData = true; + replyPrivate->state = QHttpNetworkReplyPrivate::ReadingDataState; + break;//StateMachine: goto case reading body but ignore those data; + } + if( replyPrivate->bodyLength <=0 ){ + //see QHttpNetworkHeaderPrivate::contentLength(); -1 means Content-Length not set, + qint64 skipLinesAndNextStatusLineBytes = replyPrivate->skipLinesUntillMatchNextHTTPStatusLine(socket); + if( skipLinesAndNextStatusLineBytes==-1 ){ + handleUnexpectedEOF(); + return; + } + } replyPrivate->clearHttpLayerInformation(); replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState; break; // ignore } - if (replyPrivate->shouldEmitSignals()) + if (replyPrivate->shouldEmitSignals()){ emit reply->headerChanged(); // After headerChanged had been emitted // we can suddenly have a replyPrivate->userProvidedDownloadBuffer @@ -450,7 +468,7 @@ replyPrivate->totalProgress += haveRead; // the user will get notified of it via progress signal - if (haveRead > 0) + if (!needIgnoreReadingData && haveRead > 0) emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress && replyPrivate->bodyLength > 0) { @@ -459,7 +477,7 @@ qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData); bytes += haveRead; replyPrivate->totalProgress += haveRead; - if (replyPrivate->shouldEmitSignals()) { + if (!needIgnoreReadingData && replyPrivate->shouldEmitSignals()) { emit reply->readyRead(); emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); } @@ -479,7 +497,7 @@ if (!replyPrivate->autoDecompress) { replyPrivate->totalProgress += bytes; - if (replyPrivate->shouldEmitSignals()) { + if (!needIgnoreReadingData && replyPrivate->shouldEmitSignals()) { // important: At the point of this readyRead(), the byteDatas list must be empty, // else implicit sharing will trigger memcpy when the user is reading data! emit reply->readyRead(); @@ -494,6 +512,15 @@ #endif } } + + if( needIgnoreReadingData && replyPrivate->state != QHttpNetworkReplyPrivate::ReadingDataState ){ + replyPrivate->clearHttpLayerInformation(); + replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState; + //replyPrivate->fragment.clear(); + replyPrivate->responseData.clear(); + break;//100 Continue; + } + // still in ReadingDataState? This function will be called again by the socket's readyRead if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) break; diff -r d0e4e85d1805 qhttpnetworkreply.cpp --- a/qhttpnetworkreply.cpp Sat Dec 22 19:07:01 2012 +0800 +++ b/qhttpnetworkreply.cpp Sat Dec 22 19:14:27 2012 +0800 @@ -487,6 +487,17 @@ fragment.reserve(32); } + else if( fragment.startsWith("HTTP/") ){//data from skipLinesUntillMatchNextHTTPStatusLine; + bool ok = parseStatus(fragment); + state = ReadingHeaderState; + fragment.clear(); + if (!ok) { + return -1; + } + return fragment.size(); + } + + qint64 bytes = 0; char c; qint64 haveRead = 0; @@ -499,7 +510,7 @@ break; // read more later //else if (haveRead == 1 && bytes == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31)) else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31)) - //try fix CYBUG-410; + //apply QTBUG-27161 patch; continue; // Ignore all whitespace that was trailing froma previous request on that socket bytes++; @@ -568,6 +579,44 @@ return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9; } +/* + When a 100 Continue reponse has body but does NOT supply "Content-Length" header, + we do skill the 'body' line by line untill match a line which is like a "HTTP/..." status line +*/ +qint64 QHttpNetworkReplyPrivate::skipLinesUntillMatchNextHTTPStatusLine(QAbstractSocket *socket) +{ + if (fragment.isEmpty()) { + fragment.reserve(512); + } + qint64 bytes = 0; + char c = 0; + qint64 haveRead = 0; + do { + haveRead = socket->read(&c, 1); + if (haveRead == 0) { + // read more later + break; + } else if (haveRead == -1) { + // connection broke down + return -1; + } else { + fragment.append(c); + bytes++; + + if (c == '\n') { + if( !fragment.startsWith("HTTP/") ) + fragment.clear(); + else{ + //match the next http status line after prev 100 Continue; + //keep data in fragment, for readStatus will use it; + return bytes; + } + } + } + } while (haveRead > 0); + return -1;//should never reach here; +} + qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket) { if (fragment.isEmpty()) { diff -r d0e4e85d1805 qhttpnetworkreply_p.h --- a/qhttpnetworkreply_p.h Sat Dec 22 19:07:01 2012 +0800 +++ b/qhttpnetworkreply_p.h Sat Dec 22 19:14:27 2012 +0800 @@ -178,6 +178,7 @@ ~QHttpNetworkReplyPrivate(); qint64 readStatus(QAbstractSocket *socket); bool parseStatus(const QByteArray &status); + qint64 skipLinesUntillMatchNextHTTPStatusLine(QAbstractSocket *socket); qint64 readHeader(QAbstractSocket *socket); void parseHeader(const QByteArray &header); qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);