diff options
Diffstat (limited to 'src/network/access')
-rw-r--r-- | src/network/access/access.pri | 2 | ||||
-rw-r--r-- | src/network/access/qfilenetworkreply.cpp | 205 | ||||
-rw-r--r-- | src/network/access/qfilenetworkreply_p.h | 107 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkconnection.cpp | 17 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkheader.cpp | 6 | ||||
-rw-r--r-- | src/network/access/qhttpnetworkreply.cpp | 6 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessbackend.cpp | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkaccesshttpbackend.cpp | 2 | ||||
-rw-r--r-- | src/network/access/qnetworkaccessmanager.cpp | 12 | ||||
-rw-r--r-- | src/network/access/qnetworkrequest.cpp | 105 |
10 files changed, 432 insertions, 32 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri index edc1b63..aa36890 100644 --- a/src/network/access/access.pri +++ b/src/network/access/access.pri @@ -7,6 +7,7 @@ HEADERS += access/qftp.h \ access/qhttpnetworkreply_p.h \ access/qhttpnetworkconnection_p.h \ access/qhttpnetworkconnectionchannel_p.h \ + access/qfilenetworkreply_p.h \ access/qnetworkaccessmanager.h \ access/qnetworkaccessmanager_p.h \ access/qnetworkaccesscache_p.h \ @@ -38,6 +39,7 @@ SOURCES += access/qftp.cpp \ access/qhttpnetworkreply.cpp \ access/qhttpnetworkconnection.cpp \ access/qhttpnetworkconnectionchannel.cpp \ + access/qfilenetworkreply.cpp \ access/qnetworkaccessmanager.cpp \ access/qnetworkaccesscache.cpp \ access/qnetworkaccessbackend.cpp \ diff --git a/src/network/access/qfilenetworkreply.cpp b/src/network/access/qfilenetworkreply.cpp new file mode 100644 index 0000000..497519f --- /dev/null +++ b/src/network/access/qfilenetworkreply.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfilenetworkreply_p.h" + +#include "QtCore/qdatetime.h" +#include <QtCore/QCoreApplication> +#include <QtCore/QFileInfo> + +QT_BEGIN_NAMESPACE + +QFileNetworkReplyPrivate::QFileNetworkReplyPrivate() + : QNetworkReplyPrivate(), realFileSize(0), finished(false) +{ +} + +QFileNetworkReply::QFileNetworkReply(QObject *parent, const QNetworkRequest &req) + : QNetworkReply(*new QFileNetworkReplyPrivate(), parent) +{ + setRequest(req); + setUrl(req.url()); + setOperation(QNetworkAccessManager::GetOperation); + QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection); + QNetworkReply::open(QIODevice::ReadOnly); +} + +QFileNetworkReply::~QFileNetworkReply() +{ +} + +// This code is mostly inspired by QNetworkAccessFileBackend +// We also use its translation context for error messages +void QFileNetworkReplyPrivate::_q_startOperation() +{ + Q_Q(QFileNetworkReply); + + QUrl url = q->url(); + if (url.host() == QLatin1String("localhost")) + url.setHost(QString()); + +#if !defined(Q_OS_WIN) + // do not allow UNC paths on Unix + if (!url.host().isEmpty()) { + // we handle only local files + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()); + q->setError(QNetworkReply::ProtocolInvalidOperationError, msg); + emit q->error(QNetworkReply::ProtocolInvalidOperationError); + doFinished(); + return; + } +#endif + if (url.path().isEmpty()) + url.setPath(QLatin1String("/")); + q->setUrl(url); + + + QString fileName = url.toLocalFile(); + if (fileName.isEmpty()) { + fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery); + } + realFile.setFileName(fileName); + + QFileInfo fi(realFile); + if (fi.isDir()) { + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString()); + q->setError(QNetworkReply::ContentOperationNotPermittedError, msg); + emit q->error(QNetworkReply::ContentOperationNotPermittedError); + doFinished(); + return; + } + + bool opened = realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered); + + // could we open the file? + if (!opened) { + QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2") + .arg(realFile.fileName(), realFile.errorString()); + + if (realFile.exists()) { + q->setError(QNetworkReply::ContentAccessDenied, msg); + emit q->error(QNetworkReply::ContentAccessDenied); + } else { + q->setError(QNetworkReply::ContentNotFoundError, msg); + emit q->error(QNetworkReply::ContentNotFoundError); + } + doFinished(); + return; + } + + realFileSize = fi.size(); + q->setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified()); + q->setHeader(QNetworkRequest::ContentLengthHeader, realFileSize); + + emit q->metaDataChanged(); + emit q->downloadProgress(realFileSize, realFileSize); + emit q->readyRead(); + doFinished(); +} + +bool QFileNetworkReplyPrivate::isFinished() const +{ + return finished; +} + +void QFileNetworkReplyPrivate::doFinished() +{ + Q_Q(QFileNetworkReply); + finished = true; + emit q->finished(); +} + + +void QFileNetworkReply::close() +{ + Q_D(QFileNetworkReply); + QNetworkReply::close(); + d->realFile.close(); + + if (!d->finished) + d->doFinished(); +} + +void QFileNetworkReply::abort() +{ + Q_D(QFileNetworkReply); + QNetworkReply::close(); + d->realFile.close(); + + if (!d->finished) + d->doFinished(); +} + +qint64 QFileNetworkReply::bytesAvailable() const +{ + Q_D(const QFileNetworkReply); + return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable(); +} + +bool QFileNetworkReply::isSequential () const +{ + return true; +} + +qint64 QFileNetworkReply::size() const +{ + Q_D(const QFileNetworkReply); + return d->realFileSize; +} + +/*! + \internal +*/ +qint64 QFileNetworkReply::readData(char *data, qint64 maxlen) +{ + Q_D(QFileNetworkReply); + qint64 ret = d->realFile.read(data, maxlen); + if (ret == 0 && bytesAvailable() == 0) + return -1; // everything had been read + else + return ret; +} + + +QT_END_NAMESPACE + +#include "moc_qfilenetworkreply_p.cpp" + diff --git a/src/network/access/qfilenetworkreply_p.h b/src/network/access/qfilenetworkreply_p.h new file mode 100644 index 0000000..831f50a --- /dev/null +++ b/src/network/access/qfilenetworkreply_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFILENETWORKREPLY_P_H +#define QFILENETWORKREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Network Access API. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qnetworkreply.h" +#include "qnetworkreply_p.h" +#include "qnetworkaccessmanager.h" +#include <QFile> + +QT_BEGIN_NAMESPACE + + +class QFileNetworkReplyPrivate; +class QFileNetworkReply: public QNetworkReply +{ + Q_OBJECT +public: + QFileNetworkReply(QObject *parent, const QNetworkRequest &req); + ~QFileNetworkReply(); + virtual void abort(); + + // reimplemented from QNetworkReply + virtual void close(); + virtual qint64 bytesAvailable() const; + virtual bool isSequential () const; + qint64 size() const; + + + virtual qint64 readData(char *data, qint64 maxlen); + + Q_DECLARE_PRIVATE(QFileNetworkReply) + Q_PRIVATE_SLOT(d_func(), void _q_startOperation()) + +}; + +class QFileNetworkReplyPrivate: public QNetworkReplyPrivate +{ +public: + QFileNetworkReplyPrivate(); + + QFile realFile; + qint64 realFileSize; + + void _q_startOperation(); + + virtual bool isFinished() const; + void doFinished(); + bool finished; + + + Q_DECLARE_PUBLIC(QFileNetworkReply) +}; + +QT_END_NAMESPACE + +#endif // QFILENETWORKREPLY_P_H diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 8dd7a00..2dbd512 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -194,11 +194,20 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) // some websites mandate an accept-language header and fail // if it is not sent. This is a problem with the website and - // not with us, but we work around this by setting a - // universal one always. + // not with us, but we work around this by setting + // one always. value = request.headerField("accept-language"); - if (value.isEmpty()) - request.setHeaderField("accept-language", "en,*"); + if (value.isEmpty()) { + QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-')); + QString acceptLanguage; + if (systemLocale == QLatin1String("C")) + acceptLanguage = QString::fromAscii("en,*"); + else if (systemLocale.startsWith(QLatin1String("en-"))) + acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale); + else + acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale); + request.setHeaderField("Accept-Language", acceptLanguage.toAscii()); + } // set the User Agent value = request.headerField("user-agent"); diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp index 68ed3e5..e9866ca 100644 --- a/src/network/access/qhttpnetworkheader.cpp +++ b/src/network/access/qhttpnetworkheader.cpp @@ -92,11 +92,10 @@ QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const { QList<QByteArray> result; - QByteArray lowerName = name.toLower(); QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(), end = fields.constEnd(); for ( ; it != end; ++it) - if (lowerName == it->first.toLower()) + if (qstricmp(name.constData(), it->first) == 0) result += it->second; return result; @@ -104,10 +103,9 @@ QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data) { - QByteArray lowerName = name.toLower(); QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(); while (it != fields.end()) { - if (lowerName == it->first.toLower()) + if (qstricmp(name.constData(), it->first) == 0) it = fields.erase(it); else ++it; diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index e990704..2b0c252 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -239,7 +239,7 @@ qint64 QHttpNetworkReplyPrivate::bytesAvailable() const bool QHttpNetworkReplyPrivate::isGzipped() { QByteArray encoding = headerField("content-encoding"); - return encoding.toLower() == "gzip"; + return qstricmp(encoding.constData(), "gzip") == 0; } void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() @@ -247,11 +247,10 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() // The header "Content-Encoding = gzip" is retained. // Content-Length is removed since the actual one send by the server is for compressed data QByteArray name("content-length"); - QByteArray lowerName = name.toLower(); QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(), end = fields.end(); while (it != end) { - if (name == it->first.toLower()) { + if (qstricmp(name.constData(), it->first.constData()) == 0) { fields.erase(it); break; } @@ -269,6 +268,7 @@ bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challeng QList<QByteArray> challenges = headerFieldValues(header); for (int i = 0; i<challenges.size(); i++) { QByteArray line = challenges.at(i); + // todo use qstrincmp if (!line.toLower().startsWith("negotiate")) challenge = line; } diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 1a92868..3e2db7a 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -141,6 +141,8 @@ QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice() // and the special backends need to access this. void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal) { + if (reply->isFinished()) + return; reply->emitUploadProgress(bytesSent, bytesTotal); } diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index bfcc299..91f9189 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -732,7 +732,7 @@ void QNetworkAccessHttpBackend::replyHeaderChanged() for (; it != end; ++it) { QByteArray value = rawHeader(it->first); if (!value.isEmpty()) { - if (it->first.toLower() == "set-cookie") + if (qstricmp(it->first.constData(), "set-cookie") == 0) value += "\n"; else value += ", "; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index b1160aa..754633d 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -52,6 +52,7 @@ #include "qnetworkaccessfilebackend_p.h" #include "qnetworkaccessdatabackend_p.h" #include "qnetworkaccessdebugpipebackend_p.h" +#include "qfilenetworkreply_p.h" #include "QtCore/qbuffer.h" #include "QtCore/qurl.h" @@ -681,6 +682,17 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera QIODevice *outgoingData) { Q_D(QNetworkAccessManager); + + // fast path for GET on file:// URLs + // Also if the scheme is empty we consider it a file. + // The QNetworkAccessFileBackend will right now only be used + // for PUT or qrc:// + if (op == QNetworkAccessManager::GetOperation + && (req.url().scheme() == QLatin1String("file") + || req.url().scheme().isEmpty())) { + return new QFileNetworkReply(this, req); + } + QNetworkRequest request = req; if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() && outgoingData && !outgoingData->isSequential()) { diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp index 86195c6..c91c608 100644 --- a/src/network/access/qnetworkrequest.cpp +++ b/src/network/access/qnetworkrequest.cpp @@ -48,6 +48,8 @@ #include "QtCore/qlocale.h" #include "QtCore/qdatetime.h" +#include <ctype.h> + QT_BEGIN_NAMESPACE /*! @@ -606,26 +608,25 @@ static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerNam { // headerName is not empty here - QByteArray lower = headerName.toLower(); - switch (lower.at(0)) { + switch (tolower(headerName.at(0))) { case 'c': - if (lower == "content-type") + if (qstricmp(headerName.constData(), "content-type") == 0) return QNetworkRequest::ContentTypeHeader; - else if (lower == "content-length") + else if (qstricmp(headerName.constData(), "content-length") == 0) return QNetworkRequest::ContentLengthHeader; - else if (lower == "cookie") + else if (qstricmp(headerName.constData(), "cookie") == 0) return QNetworkRequest::CookieHeader; break; case 'l': - if (lower == "location") + if (qstricmp(headerName.constData(), "location") == 0) return QNetworkRequest::LocationHeader; - else if (lower == "last-modified") + else if (qstricmp(headerName.constData(), "last-modified") == 0) return QNetworkRequest::LastModifiedHeader; break; case 's': - if (lower == "set-cookie") + if (qstricmp(headerName.constData(), "set-cookie") == 0) return QNetworkRequest::SetCookieHeader; break; } @@ -697,11 +698,10 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy QNetworkHeadersPrivate::RawHeadersList::ConstIterator QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const { - QByteArray lowerKey = key.toLower(); RawHeadersList::ConstIterator it = rawHeaders.constBegin(); RawHeadersList::ConstIterator end = rawHeaders.constEnd(); for ( ; it != end; ++it) - if (it->first.toLower() == lowerKey) + if (qstricmp(it->first.constData(), key.constData()) == 0) return it; return end; // not found @@ -775,10 +775,9 @@ void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders heade void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value) { - QByteArray lowerKey = key.toLower(); RawHeadersList::Iterator it = rawHeaders.begin(); while (it != rawHeaders.end()) { - if (it->first.toLower() == lowerKey) + if (qstricmp(it->first.constData(), key.constData()) == 0) it = rawHeaders.erase(it); else ++it; @@ -805,6 +804,68 @@ void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByt } } +// Fast month string to int conversion. This code +// assumes that the Month name is correct and that +// the string is at least three chars long. +static int name_to_month(const char* month_str) +{ + switch (month_str[0]) { + case 'J': + switch (month_str[1]) { + case 'a': + return 1; + break; + case 'u': + switch (month_str[2] ) { + case 'n': + return 6; + break; + case 'l': + return 7; + break; + } + } + break; + case 'F': + return 2; + break; + case 'M': + switch (month_str[2] ) { + case 'r': + return 3; + break; + case 'y': + return 5; + break; + } + break; + case 'A': + switch (month_str[1]) { + case 'p': + return 4; + break; + case 'u': + return 8; + break; + } + break; + case 'O': + return 10; + break; + case 'S': + return 9; + break; + case 'N': + return 11; + break; + case 'D': + return 12; + break; + } + + return 0; +} + QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) { // HTTP dates have three possible formats: @@ -820,16 +881,20 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value) // no comma -> asctime(3) format dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate); } else { - // eat the weekday, the comma and the space following it - QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); - - QLocale c = QLocale::c(); - if (pos == 3) - // must be RFC 1123 date - dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT")); - else + // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the + // QtWebKit performance benchmarks to get an idea. + if (pos == 3) { + char month_name[4]; + int day, year, hour, minute, second; + if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6) + dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second)); + } else { + QLocale c = QLocale::c(); + // eat the weekday, the comma and the space following it + QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2); // must be RFC 850 date dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'")); + } } #endif // QT_NO_DATESTRING |