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); |
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); |