summaryrefslogtreecommitdiffstats
path: root/src/network/access/qnetworkreplynsurlconnectionimpl.mm
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2013-11-26 22:30:27 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2013-11-26 22:35:48 +0100
commit4a8273a6fc2e741e811cf5dabc9a3c240306cf7f (patch)
tree2148abc88f8543eecdc0b97b2dd92594836af9b2 /src/network/access/qnetworkreplynsurlconnectionimpl.mm
parent036c5db468164297d213764c59a4b59daa76d90a (diff)
parent1c2be58fecaff1de5f2849192eb712984ebd59bd (diff)
Merge remote-tracking branch 'origin/stable' into dev
For the conflicts in msvc_nmake.cpp the ifdefs are extended since we need to support windows phone in the target branch while it is not there in the current stable branch (as of Qt 5.2). Conflicts: configure qmake/generators/win32/msvc_nmake.cpp src/3rdparty/angle/src/libEGL/Surface.cpp src/angle/src/common/common.pri src/corelib/global/qglobal.h src/corelib/io/qstandardpaths.cpp src/plugins/platforms/qnx/qqnxintegration.cpp src/plugins/platforms/qnx/qqnxscreeneventhandler.h src/plugins/platforms/xcb/qglxintegration.h src/widgets/kernel/win.pri tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp tests/auto/corelib/tools/qdatetime/tst_qdatetime.cpp tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp tools/configure/configureapp.cpp Change-Id: I00b579eefebaf61d26ab9b00046d2b5bd5958812
Diffstat (limited to 'src/network/access/qnetworkreplynsurlconnectionimpl.mm')
-rw-r--r--src/network/access/qnetworkreplynsurlconnectionimpl.mm449
1 files changed, 449 insertions, 0 deletions
diff --git a/src/network/access/qnetworkreplynsurlconnectionimpl.mm b/src/network/access/qnetworkreplynsurlconnectionimpl.mm
new file mode 100644
index 0000000000..293a505912
--- /dev/null
+++ b/src/network/access/qnetworkreplynsurlconnectionimpl.mm
@@ -0,0 +1,449 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** 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, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnetworkreplynsurlconnectionimpl_p.h"
+
+#include "QtCore/qdatetime.h"
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <Foundation/Foundation.h>
+
+QT_BEGIN_NAMESPACE
+
+// Network reply implementation using NSUrlConnection.
+//
+// Class/object structure:
+//
+// QNetworkReplyNSURLConnectionImpl
+// |- QNetworkReplyNSURLConnectionImplPrivate
+// |- (bytes read)
+// |- (QIODevice and CFStream for async POST data transfer)
+// |- NSURLConnection
+// |- QtNSURLConnectionDelegate <NSURLConnectionDataDelegate>
+// |- NSURLResponse/NSHTTPURLResponse
+// |- (response data)
+//
+// The main entry point is the QNetworkReplyNSURLConnectionImpl constructor, which
+// receives a network request from QNetworkAccessManager. The constructor
+// creates a NSURLRequest and initiates a NSURLConnection with a QtNSURLConnectionDelegate.
+// The delegate callbacks are then called asynchronously as the request completes.
+//
+
+@class QtNSURLConnectionDelegate;
+class QNetworkReplyNSURLConnectionImplPrivate: public QNetworkReplyPrivate
+{
+public:
+ QNetworkReplyNSURLConnectionImplPrivate();
+ virtual ~QNetworkReplyNSURLConnectionImplPrivate();
+
+ Q_DECLARE_PUBLIC(QNetworkReplyNSURLConnectionImpl)
+ NSURLConnection * urlConnection;
+ QtNSURLConnectionDelegate *urlConnectionDelegate;
+ qint64 bytesRead;
+
+ // Sequental outgiong data streaming
+ QIODevice *outgoingData;
+ CFReadStreamRef readStream;
+ CFWriteStreamRef writeStream;
+ CFIndex transferBufferSize;
+
+ // Forwarding functions to the public class.
+ void setFinished();
+ void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void setRawHeader(const QByteArray &headerName, const QByteArray &value);
+ void setError(QNetworkReply::NetworkError errorCode, const QString &errorString);
+};
+
+@interface QtNSURLConnectionDelegate : NSObject
+{
+ NSURLResponse *response;
+ NSMutableData *responseData;
+ QNetworkReplyNSURLConnectionImplPrivate * replyprivate;
+}
+
+- (id)initWithQNetworkReplyNSURLConnectionImplPrivate:(QNetworkReplyNSURLConnectionImplPrivate *)a_replyPrivate ;
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_3_0)
+- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
+#endif
+- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError*)error;
+- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse*)response;
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data;
+- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten
+ totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite;
+- (NSCachedURLResponse*)connection:(NSURLConnection*)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse;
+- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)request redirectResponse:(NSURLResponse*)redirectResponse;
+- (void)connectionDidFinishLoading:(NSURLConnection*)connection;
+- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection*)connection;
+
+@end
+
+QNetworkReplyNSURLConnectionImplPrivate::QNetworkReplyNSURLConnectionImplPrivate()
+ : QNetworkReplyPrivate()
+ , urlConnection(0)
+ , urlConnectionDelegate(0)
+ , bytesRead(0)
+ , readStream(0)
+ , writeStream(0)
+ , transferBufferSize(4096)
+{
+}
+
+QNetworkReplyNSURLConnectionImplPrivate::~QNetworkReplyNSURLConnectionImplPrivate()
+{
+ [urlConnection release];
+ [urlConnectionDelegate release];
+ if (readStream)
+ CFRelease(readStream);
+ if (writeStream)
+ CFRelease(writeStream);
+}
+
+void QNetworkReplyNSURLConnectionImplPrivate::setFinished()
+{
+ q_func()->setFinished(true);
+}
+
+void QNetworkReplyNSURLConnectionImplPrivate::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
+{
+ q_func()->setHeader(header, value);
+}
+
+void QNetworkReplyNSURLConnectionImplPrivate::setRawHeader(const QByteArray &headerName, const QByteArray &value)
+{
+ q_func()->setRawHeader(headerName, value);
+}
+
+void QNetworkReplyNSURLConnectionImplPrivate::setError(QNetworkReply::NetworkError errorCode, const QString &errorString)
+{
+ q_func()->setError(errorCode, errorString);
+}
+
+void QNetworkReplyNSURLConnectionImpl::readyReadOutgoingData()
+{
+ Q_D(QNetworkReplyNSURLConnectionImpl);
+ int bytesRead = 0;
+ do {
+ char data[d->transferBufferSize];
+ bytesRead = d->outgoingData->read(data, d->transferBufferSize);
+ if (bytesRead <= 0)
+ break;
+ CFIndex bytesWritten = CFWriteStreamWrite(d->writeStream, reinterpret_cast<unsigned char *>(data), bytesRead);
+ if (bytesWritten != bytesRead) {
+ CFErrorRef err = CFWriteStreamCopyError(d->writeStream);
+ qWarning() << "QNetworkReplyNSURLConnectionImpl: CFWriteStreamWrite error"
+ << (err ? QString::number(CFErrorGetCode(err)) : QStringLiteral(""));
+ }
+ } while (bytesRead > 0);
+
+ if (d->outgoingData->atEnd())
+ CFWriteStreamClose(d->writeStream);
+}
+
+@interface QtNSURLConnectionDelegate ()
+
+@property (nonatomic, retain) NSURLResponse* response;
+@property (nonatomic, retain) NSMutableData* responseData;
+
+@end
+
+@implementation QtNSURLConnectionDelegate
+
+@synthesize response;
+@synthesize responseData;
+
+- (id)initWithQNetworkReplyNSURLConnectionImplPrivate:(QNetworkReplyNSURLConnectionImplPrivate *)a_replyPrivate
+{
+ if (self = [super init])
+ replyprivate = a_replyPrivate;
+ return self;
+}
+
+- (void)dealloc
+{
+ [response release];
+ [responseData release];
+ [super dealloc];
+}
+
+#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_7, __IPHONE_3_0)
+- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
+{
+ Q_UNUSED(connection)
+ Q_UNUSED(challenge)
+
+ if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+ SecTrustRef serverTrust = challenge.protectionSpace.serverTrust;
+ SecTrustResultType resultType;
+ SecTrustEvaluate(serverTrust, &resultType);
+ if (resultType == kSecTrustResultUnspecified) {
+ // All good
+ [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
+ } else if (resultType == kSecTrustResultRecoverableTrustFailure) {
+ // Certificate verification error, ask user
+ // ### TODO actually ask user
+ // (test site: https://testssl-expire.disig.sk/index.en.html)
+ qWarning() << "QNetworkReplyNSURLConnection: Certificate verification error handlig is"
+ << "not implemented. Connection will time out.";
+ } else {
+ // other error, which the default handler will handle
+ [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
+ }
+ }
+
+ [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
+}
+#endif
+
+- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
+{
+ Q_UNUSED(connection)
+
+ QNetworkReply::NetworkError qtError = QNetworkReply::UnknownNetworkError;
+ if ([error domain] == NSURLErrorDomain) {
+ switch ([error code]) {
+ case NSURLErrorTimedOut: qtError = QNetworkReply::TimeoutError; break;
+ case NSURLErrorUnsupportedURL: qtError = QNetworkReply::ProtocolUnknownError; break;
+ case NSURLErrorCannotFindHost: qtError = QNetworkReply::HostNotFoundError; break;
+ case NSURLErrorCannotConnectToHost: qtError = QNetworkReply::ConnectionRefusedError; break;
+ case NSURLErrorNetworkConnectionLost: qtError = QNetworkReply::NetworkSessionFailedError; break;
+ case NSURLErrorDNSLookupFailed: qtError = QNetworkReply::HostNotFoundError; break;
+ case NSURLErrorNotConnectedToInternet: qtError = QNetworkReply::NetworkSessionFailedError; break;
+ case NSURLErrorUserAuthenticationRequired: qtError = QNetworkReply::AuthenticationRequiredError; break;
+ default: break;
+ }
+ }
+
+ replyprivate->setError(qtError, QString::fromNSString([error localizedDescription]));
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)aResponse
+{
+ Q_UNUSED(connection)
+ self.response = aResponse;
+ self.responseData = [NSMutableData data];
+
+ // copy headers
+ if ([aResponse isKindOfClass:[NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)aResponse;
+ NSDictionary *headers = [httpResponse allHeaderFields];
+ for (NSString *key in headers) {
+ NSString *value = [headers objectForKey:key];
+ replyprivate->setRawHeader(QString::fromNSString(key).toUtf8(), QString::fromNSString(value).toUtf8());
+ }
+ } else {
+ if ([aResponse expectedContentLength] != NSURLResponseUnknownLength)
+ replyprivate->setHeader(QNetworkRequest::ContentLengthHeader, [aResponse expectedContentLength]);
+ }
+
+ QMetaObject::invokeMethod(replyprivate->q_func(), "metaDataChanged", Qt::QueuedConnection);
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
+{
+ Q_UNUSED(connection)
+ [responseData appendData:data];
+
+ if ([response expectedContentLength] != NSURLResponseUnknownLength) {
+ QMetaObject::invokeMethod(replyprivate->q_func(), "downloadProgress", Qt::QueuedConnection,
+ Q_ARG(qint64, qint64([responseData length])),
+ Q_ARG(qint64, qint64([response expectedContentLength])));
+ }
+
+ QMetaObject::invokeMethod(replyprivate->q_func(), "readyRead", Qt::QueuedConnection);
+}
+
+- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten
+ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+{
+ Q_UNUSED(connection)
+ Q_UNUSED(bytesWritten)
+ QMetaObject::invokeMethod(replyprivate->q_func(), "uploadProgress", Qt::QueuedConnection,
+ Q_ARG(qint64, qint64(totalBytesWritten)),
+ Q_ARG(qint64, qint64(totalBytesExpectedToWrite)));
+}
+
+- (NSCachedURLResponse*)connection:(NSURLConnection*)connection willCacheResponse:(NSCachedURLResponse*)cachedResponse
+{
+ Q_UNUSED(connection)
+ return cachedResponse;
+}
+
+- (NSURLRequest*)connection:(NSURLConnection*)connection willSendRequest:(NSURLRequest*)request redirectResponse:(NSURLResponse*)redirectResponse
+{
+ Q_UNUSED(connection)
+ Q_UNUSED(redirectResponse)
+ return request;
+}
+
+- (void)connectionDidFinishLoading:(NSURLConnection*)connection
+{
+ Q_UNUSED(connection)
+ replyprivate->setFinished();
+ QMetaObject::invokeMethod(replyprivate->q_func(), "finished", Qt::QueuedConnection);
+}
+
+- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection*)connection
+{
+ Q_UNUSED(connection)
+ return YES;
+}
+
+@end
+
+QNetworkReplyNSURLConnectionImpl::~QNetworkReplyNSURLConnectionImpl()
+{
+}
+
+QNetworkReplyNSURLConnectionImpl::QNetworkReplyNSURLConnectionImpl(QObject *parent,
+ const QNetworkRequest &request, const QNetworkAccessManager::Operation operation, QIODevice* outgoingData)
+ : QNetworkReply(*new QNetworkReplyNSURLConnectionImplPrivate(), parent)
+{
+ setRequest(request);
+ setUrl(request.url());
+ setOperation(operation);
+ QNetworkReply::open(QIODevice::ReadOnly);
+
+ QNetworkReplyNSURLConnectionImplPrivate *d = (QNetworkReplyNSURLConnectionImplPrivate*) d_func();
+
+ QUrl url = request.url();
+ if (url.host() == QLatin1String("localhost"))
+ url.setHost(QString());
+
+ if (url.path().isEmpty())
+ url.setPath(QLatin1String("/"));
+ setUrl(url);
+
+ // Create a NSMutableURLRequest from QNetworkRequest
+ NSMutableURLRequest *nsRequest = [NSMutableURLRequest requestWithURL:request.url().toNSURL()
+ cachePolicy:NSURLRequestUseProtocolCachePolicy
+ timeoutInterval:60.0];
+ // copy headers
+ foreach (const QByteArray &header, request.rawHeaderList()) {
+ QByteArray headerValue = request.rawHeader(header);
+ [nsRequest addValue:QString::fromUtf8(headerValue).toNSString()
+ forHTTPHeaderField:QString::fromUtf8(header).toNSString()];
+ }
+
+ if (operation == QNetworkAccessManager::GetOperation)
+ [nsRequest setHTTPMethod:@"GET"];
+ else if (operation == QNetworkAccessManager::PostOperation)
+ [nsRequest setHTTPMethod:@"POST"];
+ else if (operation == QNetworkAccessManager::PutOperation)
+ [nsRequest setHTTPMethod:@"PUT"];
+ else if (operation == QNetworkAccessManager::DeleteOperation)
+ [nsRequest setHTTPMethod:@"DELETE"];
+ else
+ qWarning() << "QNetworkReplyNSURLConnection: Unsupported netork operation" << operation;
+
+ if (outgoingData) {
+ d->outgoingData = outgoingData;
+ if (outgoingData->isSequential()) {
+ // set up streaming from outgoingData iodevice to request
+ CFStreamCreateBoundPair(kCFAllocatorDefault, &d->readStream, &d->writeStream, d->transferBufferSize);
+ CFWriteStreamOpen(d->writeStream);
+ [nsRequest setHTTPBodyStream:reinterpret_cast<NSInputStream *>(d->readStream)];
+ connect(outgoingData, SIGNAL(readyRead()), this, SLOT(readyReadOutgoingData()));
+ readyReadOutgoingData();
+ } else {
+ // move all data at once
+ QByteArray data = outgoingData->readAll();
+ [nsRequest setHTTPBody:[NSData dataWithBytes:data.constData() length:data.length()]];
+ }
+ }
+
+ // Create connection
+ d->urlConnectionDelegate = [[QtNSURLConnectionDelegate alloc] initWithQNetworkReplyNSURLConnectionImplPrivate:d];
+ d->urlConnection = [[NSURLConnection alloc] initWithRequest:nsRequest delegate:d->urlConnectionDelegate];
+ if (!d->urlConnection) {
+ // ### what type of error is an initWithRequest fail?
+ setError(QNetworkReply::ProtocolUnknownError, QStringLiteral("QNetworkReplyNSURLConnection internal error"));
+ }
+}
+
+void QNetworkReplyNSURLConnectionImpl::close()
+{
+ // No-op? Network ops should continue (especially POSTs)
+ QNetworkReply::close();
+}
+
+void QNetworkReplyNSURLConnectionImpl::abort()
+{
+ Q_D(QNetworkReplyNSURLConnectionImpl);
+ [d->urlConnection cancel];
+ QNetworkReply::close();
+}
+
+qint64 QNetworkReplyNSURLConnectionImpl::bytesAvailable() const
+{
+ Q_D(const QNetworkReplyNSURLConnectionImpl);
+ qint64 available = QNetworkReply::bytesAvailable() +
+ [[d->urlConnectionDelegate responseData] length] -
+ d->bytesRead;
+
+ return available;
+}
+
+bool QNetworkReplyNSURLConnectionImpl::isSequential() const
+{
+ return true;
+}
+
+qint64 QNetworkReplyNSURLConnectionImpl::size() const
+{
+ Q_D(const QNetworkReplyNSURLConnectionImpl);
+ return [[d->urlConnectionDelegate responseData] length];
+}
+
+/*!
+ \internal
+*/
+qint64 QNetworkReplyNSURLConnectionImpl::readData(char *data, qint64 maxlen)
+{
+ Q_D(QNetworkReplyNSURLConnectionImpl);
+ qint64 dataSize = [[d->urlConnectionDelegate responseData] length];
+ qint64 canRead = qMin(maxlen, dataSize - d->bytesRead);
+ const char *sourceBase = static_cast<const char *>([[d->urlConnectionDelegate responseData] bytes]);
+ memcpy(data, sourceBase + d->bytesRead, canRead);
+ d->bytesRead += canRead;
+ return canRead;
+}
+
+QT_END_NAMESPACE