summaryrefslogtreecommitdiff
path: root/src/network/access/qhttpnetworkconnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qhttpnetworkconnection.cpp')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp1127
1 files changed, 10 insertions, 1117 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index edb29883c3..5940fba73e 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -45,7 +45,7 @@
#include <private/qauthenticator_p.h>
#include <qnetworkproxy.h>
#include <qauthenticator.h>
-#include <qbytearraymatcher.h>
+
#include <qbuffer.h>
#include <qpair.h>
#include <qhttp.h>
@@ -59,875 +59,9 @@
# include <QtNetwork/qsslconfiguration.h>
#endif
-#ifndef QT_NO_COMPRESS
-# include <zlib.h>
-static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
-// gzip flag byte
-#define HEAD_CRC 0x02 // bit 1 set: header CRC present
-#define EXTRA_FIELD 0x04 // bit 2 set: extra field present
-#define ORIG_NAME 0x08 // bit 3 set: original file name present
-#define COMMENT 0x10 // bit 4 set: file comment present
-#define RESERVED 0xE0 // bits 5..7: reserved
-#define CHUNK 16384
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QHttpNetworkHeaderPrivate : public QSharedData
-{
-public:
- QUrl url;
- QList<QPair<QByteArray, QByteArray> > fields;
-
- QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
- QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
- inline qint64 contentLength() const;
- inline void setContentLength(qint64 length);
-
- inline QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- inline QList<QByteArray> headerFieldValues(const QByteArray &name) const;
- inline void setHeaderField(const QByteArray &name, const QByteArray &data);
- bool operator==(const QHttpNetworkHeaderPrivate &other) const;
-
-};
-
-QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
- :url(newUrl)
-{
-}
-
-QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
- :QSharedData(other)
-{
- url = other.url;
- fields = other.fields;
-}
-
-qint64 QHttpNetworkHeaderPrivate::contentLength() const
-{
- bool ok = false;
- QByteArray value = headerField("content-length");
- qint64 length = value.toULongLong(&ok);
- if (ok)
- return length;
- return -1; // the header field is not set
-}
-
-void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
-{
- setHeaderField("Content-Length", QByteArray::number(length));
-}
-
-QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- QList<QByteArray> allValues = headerFieldValues(name);
- if (allValues.isEmpty())
- return defaultValue;
-
- QByteArray result;
- bool first = true;
- foreach (QByteArray value, allValues) {
- if (!first)
- result += ", ";
- first = false;
- result += value;
- }
- return result;
-}
-
-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())
- result += it->second;
-
- return result;
-}
-
-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())
- it = fields.erase(it);
- else
- ++it;
- }
- fields.append(qMakePair(name, data));
-}
-
-bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
-{
- return (url == other.url);
-}
-
-// QHttpNetworkRequestPrivate
-class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
-{
-public:
- QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
- QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
- QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
- ~QHttpNetworkRequestPrivate();
- bool operator==(const QHttpNetworkRequestPrivate &other) const;
- QByteArray methodName() const;
- QByteArray uri(bool throughProxy) const;
-
- static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
-
- QHttpNetworkRequest::Operation operation;
- QHttpNetworkRequest::Priority priority;
- mutable QIODevice *data;
- bool autoDecompress;
-};
-
-QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
- QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0),
- autoDecompress(false)
-{
-}
-
-QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
- : QHttpNetworkHeaderPrivate(other)
-{
- operation = other.operation;
- priority = other.priority;
- data = other.data;
- autoDecompress = other.autoDecompress;
-}
-
-QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
-{
-}
-
-bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
-{
- return QHttpNetworkHeaderPrivate::operator==(other)
- && (operation == other.operation)
- && (data == other.data);
-}
-
-QByteArray QHttpNetworkRequestPrivate::methodName() const
-{
- QByteArray ba;
- switch (operation) {
- case QHttpNetworkRequest::Options:
- ba += "OPTIONS";
- break;
- case QHttpNetworkRequest::Get:
- ba += "GET";
- break;
- case QHttpNetworkRequest::Head:
- ba += "HEAD";
- break;
- case QHttpNetworkRequest::Post:
- ba += "POST";
- break;
- case QHttpNetworkRequest::Put:
- ba += "PUT";
- break;
- case QHttpNetworkRequest::Delete:
- ba += "DELETE";
- break;
- case QHttpNetworkRequest::Trace:
- ba += "TRACE";
- break;
- case QHttpNetworkRequest::Connect:
- ba += "CONNECT";
- break;
- default:
- break;
- }
- return ba;
-}
-
-QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
-{
- QUrl::FormattingOptions format(QUrl::RemoveFragment);
-
- // for POST, query data is send as content
- if (operation == QHttpNetworkRequest::Post && !data)
- format |= QUrl::RemoveQuery;
- // for requests through proxy, the Request-URI contains full url
- if (throughProxy)
- format |= QUrl::RemoveUserInfo;
- else
- format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
- QByteArray uri = url.toEncoded(format);
- if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
- uri += '/';
- return uri;
-}
-
-QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
-{
- QByteArray ba = request.d->methodName();
- QByteArray uri = request.d->uri(throughProxy);
- ba += " " + uri;
-
- QString majorVersion = QString::number(request.majorVersion());
- QString minorVersion = QString::number(request.minorVersion());
- ba += " HTTP/" + majorVersion.toLatin1() + "." + minorVersion.toLatin1() + "\r\n";
-
- QList<QPair<QByteArray, QByteArray> > fields = request.header();
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- for (; it != fields.constEnd(); ++it)
- ba += it->first + ": " + it->second + "\r\n";
- if (request.d->operation == QHttpNetworkRequest::Post) {
- // add content type, if not set in the request
- if (request.headerField("content-type").isEmpty())
- ba += "Content-Type: application/x-www-form-urlencoded\r\n";
- if (!request.d->data && request.d->url.hasQuery()) {
- QByteArray query = request.d->url.encodedQuery();
- ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n";
- ba += "\r\n";
- ba += query;
- } else {
- ba += "\r\n";
- }
- } else {
- ba += "\r\n";
- }
- return ba;
-}
-
-class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
-{
-public:
- QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
- ~QHttpNetworkReplyPrivate();
- qint64 readStatus(QAbstractSocket *socket);
- void parseStatus(const QByteArray &status);
- qint64 readHeader(QAbstractSocket *socket);
- void parseHeader(const QByteArray &header);
- qint64 readBody(QAbstractSocket *socket, QIODevice *out);
- bool findChallenge(bool forProxy, QByteArray &challenge) const;
- QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
- void clear();
-
- qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size);
- qint64 transferChunked(QIODevice *in, QIODevice *out);
- qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
-
- qint64 bytesAvailable() const;
- bool isChunked();
- bool connectionCloseEnabled();
- bool isGzipped();
-#ifndef QT_NO_COMPRESS
- bool gzipCheckHeader(QByteArray &content, int &pos);
- int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated);
-#endif
- void removeAutoDecompressHeader();
-
- enum ReplyState {
- NothingDoneState,
- ReadingStatusState,
- ReadingHeaderState,
- ReadingDataState,
- AllDoneState
- } state;
-
- QHttpNetworkRequest request;
- int statusCode;
- int majorVersion;
- int minorVersion;
- QString errorString;
- QString reasonPhrase;
- qint64 bodyLength;
- qint64 contentRead;
- qint64 totalProgress;
- QByteArray fragment;
- qint64 currentChunkSize;
- qint64 currentChunkRead;
- QPointer<QHttpNetworkConnection> connection;
- bool initInflate;
- bool streamEnd;
-#ifndef QT_NO_COMPRESS
- z_stream inflateStrm;
-#endif
- bool autoDecompress;
-
- QByteArray responseData; // uncompressed body
- QByteArray compressedData; // compressed body (temporary)
- QBuffer requestDataBuffer;
- bool requestIsBuffering;
- bool requestIsPrepared;
-};
-
-QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
- : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
- majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
- currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
- autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false)
-{
-}
-
-QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
-{
-}
-
-void QHttpNetworkReplyPrivate::clear()
-{
- state = NothingDoneState;
- statusCode = 100;
- bodyLength = 0;
- contentRead = 0;
- totalProgress = 0;
- currentChunkSize = 0;
- currentChunkRead = 0;
- connection = 0;
-#ifndef QT_NO_COMPRESS
- if (initInflate)
- inflateEnd(&inflateStrm);
-#endif
- initInflate = false;
- streamEnd = false;
- autoDecompress = false;
- fields.clear();
-}
-
-// QHttpNetworkReplyPrivate
-qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
-{
- return (state != ReadingDataState ? 0 : fragment.size());
-}
-bool QHttpNetworkReplyPrivate::isGzipped()
-{
- QByteArray encoding = headerField("content-encoding");
- return encoding.toLower() == "gzip";
-}
-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()) {
- fields.erase(it);
- break;
- }
- ++it;
- }
-
-}
-
-bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
-{
- challenge.clear();
- // find out the type of authentication protocol requested.
- QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
- // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i);
- if (!line.toLower().startsWith("negotiate"))
- challenge = line;
- }
- return !challenge.isEmpty();
-}
-
-QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
-{
- // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
- QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
- QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i).trimmed().toLower();
- if (method < QAuthenticatorPrivate::Basic
- && line.startsWith("basic")) {
- method = QAuthenticatorPrivate::Basic;
- } else if (method < QAuthenticatorPrivate::Ntlm
- && line.startsWith("ntlm")) {
- method = QAuthenticatorPrivate::Ntlm;
- } else if (method < QAuthenticatorPrivate::DigestMd5
- && line.startsWith("digest")) {
- method = QAuthenticatorPrivate::DigestMd5;
- }
- }
- return method;
-}
-
-#ifndef QT_NO_COMPRESS
-bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
-{
- int method = 0; // method byte
- int flags = 0; // flags byte
- bool ret = false;
-
- // Assure two bytes in the buffer so we can peek ahead -- handle case
- // where first byte of header is at the end of the buffer after the last
- // gzip segment
- pos = -1;
- QByteArray &body = content;
- int maxPos = body.size()-1;
- if (maxPos < 1) {
- return ret;
- }
-
- // Peek ahead to check the gzip magic header
- if (body[0] != char(gz_magic[0]) ||
- body[1] != char(gz_magic[1])) {
- return ret;
- }
- pos += 2;
- // Check the rest of the gzip header
- if (++pos <= maxPos)
- method = body[pos];
- if (pos++ <= maxPos)
- flags = body[pos];
- if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
- return ret;
- }
-
- // Discard time, xflags and OS code:
- pos += 6;
- if (pos > maxPos)
- return ret;
- if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
- unsigned len = (unsigned)body[++pos];
- len += ((unsigned)body[++pos])<<8;
- pos += len;
- if (pos > maxPos)
- return ret;
- }
- if ((flags & ORIG_NAME) != 0) { // skip the original file name
- while(++pos <= maxPos && body[pos]) {}
- }
- if ((flags & COMMENT) != 0) { // skip the .gz file comment
- while(++pos <= maxPos && body[pos]) {}
- }
- if ((flags & HEAD_CRC) != 0) { // skip the header crc
- pos += 2;
- if (pos > maxPos)
- return ret;
- }
- ret = (pos < maxPos); // return failed, if no more bytes left
- return ret;
-}
-
-int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
-{
- int ret = Z_DATA_ERROR;
- unsigned have;
- unsigned char out[CHUNK];
- int pos = -1;
-
- if (!initInflate) {
- // check the header
- if (!gzipCheckHeader(compressed, pos))
- return ret;
- // allocate inflate state
- inflateStrm.zalloc = Z_NULL;
- inflateStrm.zfree = Z_NULL;
- inflateStrm.opaque = Z_NULL;
- inflateStrm.avail_in = 0;
- inflateStrm.next_in = Z_NULL;
- ret = inflateInit2(&inflateStrm, -MAX_WBITS);
- if (ret != Z_OK)
- return ret;
- initInflate = true;
- streamEnd = false;
- }
-
- //remove the header.
- compressed.remove(0, pos+1);
- // expand until deflate stream ends
- inflateStrm.next_in = (unsigned char *)compressed.data();
- inflateStrm.avail_in = compressed.size();
- do {
- inflateStrm.avail_out = sizeof(out);
- inflateStrm.next_out = out;
- ret = inflate(&inflateStrm, Z_NO_FLUSH);
- switch (ret) {
- case Z_NEED_DICT:
- ret = Z_DATA_ERROR;
- // and fall through
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- inflateEnd(&inflateStrm);
- initInflate = false;
- return ret;
- }
- have = sizeof(out) - inflateStrm.avail_out;
- inflated.append(QByteArray((const char *)out, have));
- } while (inflateStrm.avail_out == 0);
- // clean up and return
- if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
- inflateEnd(&inflateStrm);
- initInflate = false;
- }
- streamEnd = (ret == Z_STREAM_END);
- return ret;
-}
-#endif
-
-qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
-{
- qint64 bytes = 0;
- char c;
-
- while (socket->bytesAvailable()) {
- // allow both CRLF & LF (only) line endings
- if (socket->peek(&c, 1) == 1 && c == '\n') {
- bytes += socket->read(&c, 1); // read the "n"
- // remove the CR at the end
- if (fragment.endsWith('\r')) {
- fragment.truncate(fragment.length()-1);
- }
- parseStatus(fragment);
- state = ReadingHeaderState;
- fragment.clear(); // next fragment
- break;
- } else {
- c = 0;
- bytes += socket->read(&c, 1);
- fragment.append(c);
- }
- }
- return bytes;
-}
-
-void QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
-{
- const QByteArrayMatcher sp(" ");
- int i = sp.indexIn(status);
- const QByteArray version = status.mid(0, i);
- int j = sp.indexIn(status, i + 1);
- const QByteArray code = status.mid(i + 1, j - i - 1);
- const QByteArray reason = status.mid(j + 1, status.count() - j);
-
- const QByteArrayMatcher slash("/");
- int k = slash.indexIn(version);
- const QByteArrayMatcher dot(".");
- int l = dot.indexIn(version, k);
- const QByteArray major = version.mid(k + 1, l - k - 1);
- const QByteArray minor = version.mid(l + 1, version.count() - l);
-
- majorVersion = QString::fromAscii(major.constData()).toInt();
- minorVersion = QString::fromAscii(minor.constData()).toInt();
- statusCode = QString::fromAscii(code.constData()).toInt();
- reasonPhrase = QString::fromAscii(reason.constData());
-}
-
-qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
-{
- qint64 bytes = 0;
- char crlfcrlf[5];
- crlfcrlf[4] = '\0';
- char c = 0;
- bool allHeaders = false;
- while (!allHeaders && socket->bytesAvailable()) {
- if (socket->peek(&c, 1) == 1 && c == '\n') {
- // check for possible header endings. As per HTTP rfc,
- // the header endings will be marked by CRLFCRLF. But
- // we will allow CRLFLF, LFLF & CRLFCRLF
- if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
- allHeaders = true;
- }
- bytes += socket->read(&c, 1);
- fragment.append(c);
- }
- // we received all headers now parse them
- if (allHeaders) {
- parseHeader(fragment);
- state = ReadingDataState;
- fragment.clear(); // next fragment
- bodyLength = contentLength(); // cache the length
- }
- return bytes;
-}
-
-void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
-{
- // see rfc2616, sec 4 for information about HTTP/1.1 headers.
- // allows relaxed parsing here, accepts both CRLF & LF line endings
- const QByteArrayMatcher lf("\n");
- const QByteArrayMatcher colon(":");
- int i = 0;
- while (i < header.count()) {
- int j = colon.indexIn(header, i); // field-name
- if (j == -1)
- break;
- const QByteArray field = header.mid(i, j - i).trimmed();
- j++;
- // any number of LWS is allowed before and after the value
- QByteArray value;
- do {
- i = lf.indexIn(header, j);
- if (i == -1)
- break;
- if (!value.isEmpty())
- value += ' ';
- // check if we have CRLF or only LF
- bool hasCR = (i && header[i-1] == '\r');
- int length = i -(hasCR ? 1: 0) - j;
- value += header.mid(j, length).trimmed();
- j = ++i;
- } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
- if (i == -1)
- break; // something is wrong
-
- fields.append(qMakePair(field, value));
- }
-}
-
-bool QHttpNetworkReplyPrivate::isChunked()
-{
- return headerField("transfer-encoding").toLower().contains("chunked");
-}
-
-bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
-{
- return (headerField("connection").toLower().contains("close") ||
- headerField("proxy-connection").toLower().contains("close"));
-}
-
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
-{
- qint64 bytes = 0;
- if (isChunked()) {
- bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
- } else if (bodyLength > 0) { // we have a Content-Length
- bytes += transferRaw(socket, out, bodyLength - contentRead);
- if (contentRead + bytes == bodyLength)
- state = AllDoneState;
- } else {
- bytes += transferRaw(socket, out, socket->bytesAvailable());
- }
- if (state == AllDoneState)
- socket->readAll(); // Read the rest to clean (CRLF)
- contentRead += bytes;
- return bytes;
-}
-
-qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size)
-{
- qint64 bytes = 0;
- Q_ASSERT(in);
- Q_ASSERT(out);
-
- int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
- QByteArray raw(toBeRead, 0);
- while (size > 0) {
- qint64 read = in->read(raw.data(), raw.size());
- if (read == 0)
- return bytes;
- // ### error checking here
- qint64 written = out->write(raw.data(), read);
- if (written == 0)
- return bytes;
- if (read != written)
- qDebug() << "### read" << read << "written" << written;
- bytes += read;
- size -= read;
- out->waitForBytesWritten(-1); // throttle
- }
- return bytes;
-
-}
-
-qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *out)
-{
- qint64 bytes = 0;
- while (in->bytesAvailable()) { // while we can read from input
- // if we are done with the current chunk, get the size of the new chunk
- if (currentChunkRead >= currentChunkSize) {
- currentChunkSize = 0;
- currentChunkRead = 0;
- if (bytes) {
- char crlf[2];
- bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
- }
- bytes += getChunkSize(in, &currentChunkSize);
- if (currentChunkSize == -1)
- break;
- }
- // if the chunk size is 0, end of the stream
- if (currentChunkSize == 0) {
- state = AllDoneState;
- break;
- }
- // otherwise, read data
- qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
- QByteArray buffer(readSize, 0);
- qint64 read = in->read(buffer.data(), readSize);
- bytes += read;
- currentChunkRead += read;
- qint64 written = out->write(buffer);
- Q_UNUSED(written); // Avoid compile warning when building release
- Q_ASSERT(read == written);
- // ### error checking here
- out->waitForBytesWritten(-1);
- }
- return bytes;
-}
-
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
-{
- qint64 bytes = 0;
- char crlf[2];
- *chunkSize = -1;
- int bytesAvailable = in->bytesAvailable();
- while (bytesAvailable > bytes) {
- qint64 sniffedBytes = in->peek(crlf, 2);
- int fragmentSize = fragment.size();
- // check the next two bytes for a "\r\n", skip blank lines
- if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
- ||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
- {
- bytes += in->read(crlf, 1); // read the \r or \n
- if (crlf[0] == '\r')
- bytes += in->read(crlf, 1); // read the \n
- bool ok = false;
- // ignore the chunk-extension
- fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
- *chunkSize = fragment.toLong(&ok, 16);
- fragment.clear();
- break; // size done
- } else {
- // read the fragment to the buffer
- char c = 0;
- bytes += in->read(&c, 1);
- fragment.append(c);
- }
- }
- return bytes;
-}
-
-// QHttpNetworkConnectionPrivate
-
-typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
-
-class QHttpNetworkConnectionPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QHttpNetworkConnection)
-public:
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
- ~QHttpNetworkConnectionPrivate();
- void init();
- void connectSignals(QAbstractSocket *socket);
-
- enum SocketState {
- IdleState = 0, // ready to send request
- ConnectingState = 1, // connecting to host
- WritingState = 2, // writing the data
- WaitingState = 4, // waiting for reply
- ReadingState = 8, // reading the reply
- Wait4AuthState = 0x10, // blocked for send till the current authentication slot is done
- BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
- };
-
- enum { ChunkSize = 4096 };
-
- int indexOf(QAbstractSocket *socket) const;
- bool isSocketBusy(QAbstractSocket *socket) const;
- bool isSocketWriting(QAbstractSocket *socket) const;
- bool isSocketWaiting(QAbstractSocket *socket) const;
- bool isSocketReading(QAbstractSocket *socket) const;
-
- QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
- void unqueueRequest(QAbstractSocket *socket);
- void prepareRequest(HttpMessagePair &request);
- bool sendRequest(QAbstractSocket *socket);
- void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply);
- void resendCurrentRequest(QAbstractSocket *socket);
- void closeChannel(int channel);
- void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
-
- // private slots
- void _q_bytesWritten(qint64 bytes); // proceed sending
- void _q_readyRead(); // pending data to read
- void _q_disconnected(); // disconnected from host
- void _q_startNextRequest(); // send the next request from the queue
- void _q_restartPendingRequest(); // send the currently blocked request
- void _q_connected(); // start sending request
- void _q_error(QAbstractSocket::SocketError); // error from socket
-#ifndef QT_NO_NETWORKPROXY
- void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
-#endif
- void _q_dataReadyReadNoBuffer();
- void _q_dataReadyReadBuffer();
-
- void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
- bool ensureConnection(QAbstractSocket *socket);
- QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket);
- void eraseData(QHttpNetworkReply *reply);
-#ifndef QT_NO_COMPRESS
- bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
-#endif
- void bufferData(HttpMessagePair &request);
- void removeReply(QHttpNetworkReply *reply);
-
- QString hostName;
- quint16 port;
- bool encrypt;
-
- struct Channel {
- QAbstractSocket *socket;
- SocketState state;
- QHttpNetworkRequest request; // current request
- QHttpNetworkReply *reply; // current reply for this request
- qint64 written;
- qint64 bytesTotal;
- bool resendCurrent;
- int lastStatus; // last status received on this channel
- bool pendingEncrypt; // for https (send after encrypted)
- int reconnectAttempts; // maximum 2 reconnection attempts
- QAuthenticatorPrivate::Method authMehtod;
- QAuthenticatorPrivate::Method proxyAuthMehtod;
- QAuthenticator authenticator;
- QAuthenticator proxyAuthenticator;
-#ifndef QT_NO_OPENSSL
- bool ignoreSSLErrors;
-#endif
- Channel() :state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), reconnectAttempts(2),
- authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
-#ifndef QT_NO_OPENSSL
- , ignoreSSLErrors(false)
-#endif
- {}
- };
- static const int channelCount;
- Channel channels[2]; // maximum of 2 socket connections to the server
- bool pendingAuthSignal; // there is an incomplete authentication signal
- bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
-
- void appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed);
- qint64 bytesAvailable(const QHttpNetworkReply &reply, bool compressed = false) const;
- qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed);
- void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
- bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
- void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply);
- void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply);
- inline bool emitSignals(QHttpNetworkReply *reply);
- inline bool expectContent(QHttpNetworkReply *reply);
-
-#ifndef QT_NO_OPENSSL
- void _q_encrypted(); // start sending request (https)
- void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
- QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
-#endif
-
-#ifndef QT_NO_NETWORKPROXY
- QNetworkProxy networkProxy;
-#endif
-
- //The request queues
- QList<HttpMessagePair> highPriorityQueue;
- QList<HttpMessagePair> lowPriorityQueue;
-};
+QT_BEGIN_NAMESPACE
const int QHttpNetworkConnectionPrivate::channelCount = 2;
@@ -1131,6 +265,11 @@ bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket)
if (socket->state() != QAbstractSocket::ConnectedState) {
// connect to the host if not already connected.
int index = indexOf(socket);
+ // resend this request after we receive the disconnected signal
+ if (socket->state() == QAbstractSocket::ClosingState) {
+ channels[index].resendCurrent = true;
+ return false;
+ }
channels[index].state = ConnectingState;
channels[index].pendingEncrypt = encrypt;
@@ -1848,6 +987,9 @@ void QHttpNetworkConnectionPrivate::_q_disconnected()
channels[i].state = ReadingState;
if (channels[i].reply)
receiveReply(socket, channels[i].reply);
+ } else if (channels[i].state == IdleState && channels[i].resendCurrent) {
+ // re-sending request because the socket was in ClosingState
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
channels[i].state = IdleState;
}
@@ -2150,235 +1292,6 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
-// QHttpNetworkRequest
-
-QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
- : d(new QHttpNetworkRequestPrivate(operation, priority, url))
-{
-}
-
-QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
- : QHttpNetworkHeader(other), d(other.d)
-{
-}
-
-QHttpNetworkRequest::~QHttpNetworkRequest()
-{
-}
-
-QUrl QHttpNetworkRequest::url() const
-{
- return d->url;
-}
-void QHttpNetworkRequest::setUrl(const QUrl &url)
-{
- d->url = url;
-}
-
-qint64 QHttpNetworkRequest::contentLength() const
-{
- return d->contentLength();
-}
-
-void QHttpNetworkRequest::setContentLength(qint64 length)
-{
- d->setContentLength(length);
-}
-
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
-{
- return d->fields;
-}
-
-QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- return d->headerField(name, defaultValue);
-}
-
-void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
-{
- d->setHeaderField(name, data);
-}
-
-QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
-{
- d = other.d;
- return *this;
-}
-
-bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
-{
- return d->operator==(*other.d);
-}
-
-QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
-{
- return d->operation;
-}
-
-void QHttpNetworkRequest::setOperation(Operation operation)
-{
- d->operation = operation;
-}
-
-QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
-{
- return d->priority;
-}
-
-void QHttpNetworkRequest::setPriority(Priority priority)
-{
- d->priority = priority;
-}
-
-QIODevice *QHttpNetworkRequest::data() const
-{
- return d->data;
-}
-
-void QHttpNetworkRequest::setData(QIODevice *data)
-{
- d->data = data;
-}
-
-int QHttpNetworkRequest::majorVersion() const
-{
- return 1;
-}
-
-int QHttpNetworkRequest::minorVersion() const
-{
- return 1;
-}
-
-// QHttpNetworkReply
-
-QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
- : QObject(*new QHttpNetworkReplyPrivate(url), parent)
-{
-}
-
-QHttpNetworkReply::~QHttpNetworkReply()
-{
- Q_D(QHttpNetworkReply);
- if (d->connection) {
- d->connection->d_func()->removeReply(this);
- }
-}
-
-QUrl QHttpNetworkReply::url() const
-{
- return d_func()->url;
-}
-void QHttpNetworkReply::setUrl(const QUrl &url)
-{
- Q_D(QHttpNetworkReply);
- d->url = url;
-}
-
-qint64 QHttpNetworkReply::contentLength() const
-{
- return d_func()->contentLength();
-}
-
-void QHttpNetworkReply::setContentLength(qint64 length)
-{
- Q_D(QHttpNetworkReply);
- d->setContentLength(length);
-}
-
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
-{
- return d_func()->fields;
-}
-
-QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
-{
- return d_func()->headerField(name, defaultValue);
-}
-
-void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
-{
- Q_D(QHttpNetworkReply);
- d->setHeaderField(name, data);
-}
-
-void QHttpNetworkReply::parseHeader(const QByteArray &header)
-{
- Q_D(QHttpNetworkReply);
- d->parseHeader(header);
-}
-
-QHttpNetworkRequest QHttpNetworkReply::request() const
-{
- return d_func()->request;
-}
-
-void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
-{
- Q_D(QHttpNetworkReply);
- d->request = request;
-}
-
-int QHttpNetworkReply::statusCode() const
-{
- return d_func()->statusCode;
-}
-
-void QHttpNetworkReply::setStatusCode(int code)
-{
- Q_D(QHttpNetworkReply);
- d->statusCode = code;
-}
-
-QString QHttpNetworkReply::errorString() const
-{
- return d_func()->errorString;
-}
-
-QString QHttpNetworkReply::reasonPhrase() const
-{
- return d_func()->reasonPhrase;
-}
-
-void QHttpNetworkReply::setErrorString(const QString &error)
-{
- Q_D(QHttpNetworkReply);
- d->errorString = error;
-}
-
-int QHttpNetworkReply::majorVersion() const
-{
- return d_func()->majorVersion;
-}
-
-int QHttpNetworkReply::minorVersion() const
-{
- return d_func()->minorVersion;
-}
-
-qint64 QHttpNetworkReply::bytesAvailable() const
-{
- Q_D(const QHttpNetworkReply);
- if (d->connection)
- return d->connection->d_func()->bytesAvailable(*this);
- else
- return -1;
-}
-
-QByteArray QHttpNetworkReply::read(qint64 maxSize)
-{
- Q_D(QHttpNetworkReply);
- QByteArray data;
- if (d->connection)
- d->connection->d_func()->read(*this, data, maxSize, false);
- return data;
-}
-
-bool QHttpNetworkReply::isFinished() const
-{
- return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
-}
// SSL support below
#ifndef QT_NO_OPENSSL
@@ -2433,27 +1346,7 @@ void QHttpNetworkConnection::ignoreSslErrors(int channel)
}
}
-QSslConfiguration QHttpNetworkReply::sslConfiguration() const
-{
- Q_D(const QHttpNetworkReply);
- if (d->connection)
- return d->connection->d_func()->sslConfiguration(*this);
- return QSslConfiguration();
-}
-void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
-{
- Q_D(QHttpNetworkReply);
- if (d->connection)
- d->connection->setSslConfiguration(config);
-}
-
-void QHttpNetworkReply::ignoreSslErrors()
-{
- Q_D(QHttpNetworkReply);
- if (d->connection)
- d->connection->ignoreSslErrors();
-}
#endif //QT_NO_OPENSSL