diff options
author | Qt Continuous Integration System <qt-info@nokia.com> | 2010-11-26 23:01:48 (GMT) |
---|---|---|
committer | Qt Continuous Integration System <qt-info@nokia.com> | 2010-11-26 23:01:48 (GMT) |
commit | 318920abb6d0db0fe51663023b5f34aaf6d7b10c (patch) | |
tree | fa6f20deb2970976262d38894298e1bbf76c1433 | |
parent | 0f4c26ef3dc35e0b45c858b779074708de688cc6 (diff) | |
parent | 5e0c4463c8dc8857e9f64a1bdde9a94cd8c39c4d (diff) | |
download | Qt-318920abb6d0db0fe51663023b5f34aaf6d7b10c.zip Qt-318920abb6d0db0fe51663023b5f34aaf6d7b10c.tar.gz Qt-318920abb6d0db0fe51663023b5f34aaf6d7b10c.tar.bz2 |
Merge branch '4.7' of scm.dev.nokia.troll.no:qt/qt-water-team into 4.7
* '4.7' of scm.dev.nokia.troll.no:qt/qt-water-team:
Fix minor memory leak
Remove unused variable
Revert "Fix a missing error-signal when a server is shut down while downloading"
Add missing newline to configure.exe output.
Repaint when text color changes
Fix type punning warnings from gcc
Slightly improved tst_qmlvisual output
More detail when the process crashes in tst_qmlvisual.
Only patch package content that is necessary for self-signing
Fix a missing error-signal when a server is shut down while downloading
Ensure that if this is does not have a valid filter when on XP or less
HTTP backend: fix build without Qt3 support
QNetworkAccessManager: enable synchronous HTTP calls
28 files changed, 978 insertions, 217 deletions
diff --git a/bin/createpackage.pl b/bin/createpackage.pl index 522d1fb..6b83585 100755 --- a/bin/createpackage.pl +++ b/bin/createpackage.pl @@ -82,6 +82,8 @@ Where supported options are as follows: [-s|stub] = Generates stub sis for ROM. [-n|sisname <name>] = Specifies the final sis name. [-g|gcce-is-armv5] = Convert gcce platform to armv5. + [-d|dont-patch] = Skip automatic patching of capabilities and pkg file if default certificate + is used. Instead non-self-signable capabilities just cause warnings. Where parameters are as follows: templatepkg = Name of .pkg file template target = Either debug or release @@ -127,6 +129,7 @@ my $stub = ""; my $signed_sis_name = ""; my $onlyUnsigned = ""; my $convertGcce = ""; +my $dontPatchCaps = ""; unless (GetOptions('i|install' => \$install, 'p|preprocess' => \$preprocessonly, @@ -135,7 +138,8 @@ unless (GetOptions('i|install' => \$install, 'o|only-unsigned' => \$onlyUnsigned, 's|stub' => \$stub, 'n|sisname=s' => \$signed_sis_name, - 'g|gcce-is-armv5' => \$convertGcce,)) { + 'g|gcce-is-armv5' => \$convertGcce, + 'd|dont-patch' => \$dontPatchCaps,)) { Usage(); } @@ -343,9 +347,13 @@ if($stub) { && !@certificates && $templatepkg !~ m/_installer\.pkg$/i && !$onlyUnsigned) { - print("Auto-patching capabilities for self signed package.\n"); my $patch_capabilities = File::Spec->catfile(dirname($0), "patch_capabilities"); - system ("$patch_capabilities $pkgoutput") and die ("ERROR: Automatic patching failed"); + if ($dontPatchCaps) { + system ("$patch_capabilities -c $pkgoutput") and print ("Warning: Package check for self-signing viability failed. Installing the package on a device will most likely fail!\n\n"); + } else { + print("Auto-patching self-signed package.\n"); + system ("$patch_capabilities $pkgoutput") and die ("ERROR: Automatic patching failed"); + } } # Create SIS. diff --git a/bin/patch_capabilities.pl b/bin/patch_capabilities.pl index 994d493..df71339 100755 --- a/bin/patch_capabilities.pl +++ b/bin/patch_capabilities.pl @@ -63,8 +63,11 @@ sub Usage() { print(" symbian-sbsv2 platform, 'target-platform' is REQUIRED. ***\n\n"); print(" *** NOTE2: When patching gcce binaries built with symbian-sbsv2 toolchain,\n"); print(" armv5 must be specified as platform.\n"); - print("\nUsage: patch_capabilities.pl pkg_filename [target-platform [capability list]]\n"); + print("\nUsage: patch_capabilities.pl [-c] pkg_filename [target-platform [capability list]]\n"); print("\nE.g. patch_capabilities.pl myapp_template.pkg release-armv5 \"All -TCB\"\n"); + print("\nThe parameter -c can be used to just check if package is compatible with self-signing\n"); + print("without actually doing any patching.\n"); + print("Explicit capability list cannot be used with -c parameter.\n"); exit(); } @@ -86,6 +89,14 @@ if (@ARGV) { # Parse the first given script argument as a ".pkg" file name. my $pkgFileName = shift(@ARGV); + my $justCheck = ""; + my $msgPrefix = "Patching:"; + + if ($pkgFileName eq "-c") { + $pkgFileName = shift(@ARGV); + $justCheck = true; + $msgPrefix = "Warning:"; + } # These variables will only be set for template .pkg files. my $target; @@ -123,15 +134,22 @@ if (@ARGV) if (($pkgFileName =~ m|\.pkg$|i) && -r($pkgFileName)) { print ("\n"); - print ("Patching package file and relevant binaries...\n"); + if ($justCheck) { + print ("Checking"); + } else { + print ("Patching"); + } + print (" package file and relevant binaries...\n"); - # If there are more arguments given, parse them as capabilities. - if (@ARGV) - { - @capabilitiesSpecified = (); - while (@ARGV) + if (!$justCheck) { + # If there are more arguments given, parse them as capabilities. + if (@ARGV) { - push (@capabilitiesSpecified, pop(@ARGV)); + @capabilitiesSpecified = (); + while (@ARGV) + { + push (@capabilitiesSpecified, pop(@ARGV)); + } } } @@ -139,11 +157,15 @@ if (@ARGV) my @binaries = (); my $tempPkgFileName = $pkgFileName."_@@TEMP@@"; - unlink($tempPkgFileName); - open (NEW_PKG, ">>".$tempPkgFileName); + + if (!$justCheck) { + unlink($tempPkgFileName); + open (NEW_PKG, ">>".$tempPkgFileName); + } open (PKG, "<".$pkgFileName); - my $manufacturerElseBlock = 0; + my $checkFailed = ""; + my $somethingPatched = ""; # Parse each line. while (<PKG>) @@ -155,66 +177,19 @@ if (@ARGV) if ($line =~ m/^\#.*\((0x[0-7][0-9a-fA-F]*)\).*$/) { my $oldUID = $1; - my $newUID = $oldUID; - $newUID =~ s/0x./0xE/i; - $newLine =~ s/$oldUID/$newUID/; - print ("Patching: UID $oldUID is not compatible with self-signing! Changed to: $newUID.\n"); - } - - # Patch embedded sis name and UID if UID is in protected range - if ($line =~ m/^@\"*(.*\.sis).*\((0x[0-7][0-9a-fA-F]*)\).*$/) - { - my $oldSisName = $1; - my $oldUID = $2; - my $newUID = $oldUID; - $newUID =~ s/0x./0xE/i; - $newLine =~ s/$oldUID/$newUID/; - print ("Patching: Embedded sis $oldSisName UID $oldUID changed to: $newUID.\n"); - - if ($oldSisName !~ m/^.*_selfsigned.sis$/i) - { - my $newSisName = $oldSisName; - $newSisName =~ s/\.sis$/_selfsigned\.sis/i; - $newLine =~ s/$oldSisName/$newSisName/i; - print ("Patching: Embedded sis $oldSisName name changed to: $newSisName.\n"); + print ("$msgPrefix UID $oldUID is not compatible with self-signing!\n"); + + if ($justCheck) { + $checkFailed = true; + } else { + my $newUID = $oldUID; + $newUID =~ s/0x./0xE/i; + $newLine =~ s/$oldUID/$newUID/; + print ("$msgPrefix Package UID changed to: $newUID.\n"); + $somethingPatched = true; } } - # Remove dependencies to known problem packages (i.e. packages that are likely to be patched, too) - # to reduce unnecessary error messages. - if ($line =~ m/^\((0x2002af5f)\).*\{.*\}$/) - { - $newLine = "\n"; - print ("Patching: Removed dependency to sqlite3.sis ($1) to avoid installation issues in case sqlite3.sis is also patched.\n"); - } - if ($line =~ m/^\((0x2001E61C)\).*\{.*\}$/) - { - $newLine = "\n"; - print ("Patching: Removed dependency to qt.sis ($1) to avoid installation issues in case qt.sis is also patched.\n"); - } - - # Remove manufacturer ifdef - if ($line =~ m/^.*\(MANUFACTURER\)\=\(.*\).*$/) - { - $newLine = "\n"; - print ("Patching: Removed manufacturer check as it is usually not desirable in self-signed packages.\n"); - } - - if ($line =~ m/^ELSEIF.*MANUFACTURER$/) - { - $manufacturerElseBlock = 1; - } - - if ($manufacturerElseBlock eq 1) - { - $newLine = "\n"; - } - - if ($line =~ m/^ENDIF.*MANUFACTURER$/) - { - $manufacturerElseBlock = 0; - } - # If the line specifies a file, parse the source and destination locations. if ($line =~ m|^ *\"([^\"]+)\"\s*\-\s*\"([^\"]+)\"|) { @@ -231,16 +206,20 @@ if (@ARGV) $sourcePath =~ s/\$\(TARGET\)/$target/gm; } - # Change the source file name (but only if not already patched) - my $patchedSourcePath = $sourcePath; - if ($patchedSourcePath !~ m/_patched_caps/) - { - $newLine =~ s/(^.*)(\.dll|\.exe)(.*)(\.dll|\.exe)/$1_patched_caps$2$3$4/i; - $patchedSourcePath =~ s/(^.*)(\.dll|\.exe)/$1_patched_caps$2/i; - - copy($sourcePath, $patchedSourcePath) or die "$sourcePath cannot be copied for patching."; + if ($justCheck) { + push (@binaries, $sourcePath); + } else { + # Change the source file name (but only if not already patched) + my $patchedSourcePath = $sourcePath; + if ($patchedSourcePath !~ m/_patched_caps/) + { + $newLine =~ s/(^.*)(\.dll|\.exe)(.*)(\.dll|\.exe)/$1_patched_caps$2$3$4/i; + $patchedSourcePath =~ s/(^.*)(\.dll|\.exe)/$1_patched_caps$2/i; + + copy($sourcePath, $patchedSourcePath) or die "$sourcePath cannot be copied for patching."; + } + push (@binaries, $patchedSourcePath); } - push (@binaries, $patchedSourcePath); } } @@ -250,11 +229,12 @@ if (@ARGV) } close (PKG); - close (NEW_PKG); - - unlink($pkgFileName); - rename($tempPkgFileName, $pkgFileName); + if (!$justCheck) { + close (NEW_PKG); + unlink($pkgFileName); + rename($tempPkgFileName, $pkgFileName); + } print ("\n"); my $baseCommandToExecute = "elftran -vid 0x0 -capability \"%s\" "; @@ -265,18 +245,18 @@ if (@ARGV) # Create the command line for setting the capabilities. my ($binaryVolume, $binaryDirs, $binaryBaseName) = File::Spec->splitpath($binaryPath); my $commandToExecute = $baseCommandToExecute; - my $executeNeeded = 0; + my $executeNeeded = ""; if (@capabilitiesSpecified) { $commandToExecute = sprintf($baseCommandToExecute, join(" ", @capabilitiesSpecified)); - $executeNeeded = 1; + $executeNeeded = true; my $capString = join(" ", @capabilitiesSpecified); - print ("Patching: Patching the the Vendor ID to 0 and the capabilities used to: \"$capString\" in \"$binaryBaseName\".\n"); + print ("$msgPrefix Patching the the Vendor ID to 0 and the capabilities used to: \"$capString\" in \"$binaryBaseName\".\n"); } else { # Test which capabilities are present and then restrict them to the allowed set. # This avoid raising the capabilities of apps that already have none. my $dllCaps; - open($dllCaps, "elftran -dump s $binaryPath |") or die ("Could not execute elftran"); + open($dllCaps, "elftran -dump s $binaryPath |") or die ("ERROR: Could not execute elftran"); my $capsFound = 0; my $originalVid; my @capabilitiesToSet; @@ -288,8 +268,8 @@ if (@ARGV) if ($binaryBaseName =~ /\.exe$/) { # Installer refuses to install protected executables in a self signed package, so abort if one is detected. # We can't simply just patch the executable SID, as any registration resources executable uses will be linked to it via SID. - print ("Patching: Executable with SID in the protected range (0x$exeSid) detected: \"$binaryBaseName\". A self-signed sis with protected executables is not supported.\n"); - exit(1); + print ("$msgPrefix Executable with SID in the protected range (0x$exeSid) detected: \"$binaryBaseName\". A self-signed sis with protected executables is not supported.\n\n"); + $checkFailed = true; } } if (/^Vendor ID: ([0-9a-fA-F]*)$/) { @@ -302,7 +282,7 @@ if (@ARGV) if ($capabilitiesToAllow =~ /$_/) { push(@capabilitiesToSet, $_); if (Location =~ /$_/i) { - print ("Patching: Warning - \"Location\" capability detected for binary: \"$binaryBaseName\". This capability is not self-signable for S60 3rd edition feature pack 1 devices, so installing this package on those devices will most likely not work.\n"); + print ("$msgPrefix \"Location\" capability detected for binary: \"$binaryBaseName\". This capability is not self-signable for S60 3rd edition feature pack 1 devices, so installing this package on those devices will most likely not work.\n\n"); } } else { push(@capabilitiesToDrop, $_); @@ -311,22 +291,32 @@ if (@ARGV) } close($dllCaps); if ($originalVid !~ "00000000") { - print ("Patching: Vendor ID (0x$originalVid) incompatible with self-signed packages, setting it to zero for \"$binaryBaseName\".\n"); - $executeNeeded = 1; + print ("$msgPrefix Non-zero vendor ID (0x$originalVid) is incompatible with self-signed packages in \"$binaryBaseName\""); + if ($justCheck) { + print (".\n\n"); + $checkFailed = true; + } else { + print (", setting it to zero.\n\n"); + $executeNeeded = true; + } } if ($#capabilitiesToDrop) { my $capsToDropStr = join("\", \"", @capabilitiesToDrop); $capsToDropStr =~ s/\", \"$//; - if ($binaryBaseName =~ /\.exe$/) { - # While libraries often have capabilities they do not themselves need just to enable them to be loaded by wider variety of processes, - # executables are more likely to need every capability they have been assigned or they won't function correctly. - print ("Patching: Executable with capabilities incompatible with self-signing detected: \"$binaryBaseName\". (Incompatible capabilities: \"$capsToDropStr\".) Reducing capabilities is only supported for libraries.\n"); - print ("Patching: Please use a proper developer certificate for signing this package.\n"); - exit(1); + if ($justCheck) { + print ("$msgPrefix The following capabilities used in \"$binaryBaseName\" are not compatible with a self-signed package: \"$capsToDropStr\".\n\n"); + $checkFailed = true; } else { - print ("Patching: The following capabilities used in \"$binaryBaseName\" are not compatible with a self-signed package and will be removed: \"$capsToDropStr\".\n"); - $executeNeeded = 1; + if ($binaryBaseName =~ /\.exe$/) { + # While libraries often have capabilities they do not themselves need just to enable them to be loaded by wider variety of processes, + # executables are more likely to need every capability they have been assigned or they won't function correctly. + print ("$msgPrefix Executable with capabilities incompatible with self-signing detected: \"$binaryBaseName\". (Incompatible capabilities: \"$capsToDropStr\".) Reducing capabilities is only supported for libraries.\n"); + $checkFailed = true; + } else { + print ("$msgPrefix The following capabilities used in \"$binaryBaseName\" are not compatible with a self-signed package and will be removed: \"$capsToDropStr\".\n"); + $executeNeeded = true; + } } } $commandToExecute = sprintf($baseCommandToExecute, join(" ", @capabilitiesToSet)); @@ -337,16 +327,37 @@ if (@ARGV) # Actually execute the elftran command to set the capabilities. print ("\n"); system ("$commandToExecute > $nullDevice"); + $somethingPatched = true; } ## Create another command line to check that the set capabilities are correct. #$commandToExecute = "elftran -dump s ".$binaryPath; } + if ($checkFailed) { + print ("\n"); + if ($justCheck) { + print ("$msgPrefix The package is not compatible with self-signing.\n"); + } else { + print ("$msgPrefix Unable to patch the package for self-singing.\n"); + } + print ("Use a proper developer certificate for signing this package.\n\n"); + exit(1); + } + + if ($justCheck) { + print ("Package is compatible with self-signing.\n"); + } else { + if ($somethingPatched) { + print ("NOTE: A patched package may not work as expected due to reduced capabilities and other modifications,\n"); + print (" so it should not be used for any kind of Symbian signing or distribution!\n"); + print (" Use a proper certificate to avoid the need to patch the package.\n"); + } else { + print ("No patching was required!\n"); + } + } print ("\n"); - print ("NOTE: A patched package may not work as expected due to reduced capabilities and other modifications,\n"); - print (" so it should not be used for any kind of Symbian signing or distribution!\n"); - print (" Use a proper certificate to avoid the need to patch the package.\n"); - print ("\n"); + } else { + Usage(); } } else diff --git a/doc/src/platforms/symbian-introduction.qdoc b/doc/src/platforms/symbian-introduction.qdoc index 7bc5303..9da94c4 100644 --- a/doc/src/platforms/symbian-introduction.qdoc +++ b/doc/src/platforms/symbian-introduction.qdoc @@ -222,6 +222,7 @@ \row \o -s \o Generates stub sis for ROM. \row \o -n <name> \o Specifies the final sis name. \row \o -g \o Treat gcce platform as armv5. + \row \o -d \o Skip automatic patching of the package when default certificate is used. \endtable Execute the \c{createpackage.pl} script without any diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp index c4b51f2..c219f1d 100644 --- a/qmake/generators/symbian/symmake_sbsv2.cpp +++ b/qmake/generators/symbian/symmake_sbsv2.cpp @@ -377,7 +377,6 @@ void SymbianSbsv2MakefileGenerator::writeWrapperMakefile(QFile& wrapperFile, boo t << qmakeCmd << endl; t << endl; - QString currentClause; QString locFileDep = generateLocFileTarget(t, qmakeCmd); t << "debug: " << locFileDep << BLD_INF_FILENAME << endl; diff --git a/src/3rdparty/phonon/ds9/videorenderer_evr.cpp b/src/3rdparty/phonon/ds9/videorenderer_evr.cpp index de3f46f..ff39eccc4 100644 --- a/src/3rdparty/phonon/ds9/videorenderer_evr.cpp +++ b/src/3rdparty/phonon/ds9/videorenderer_evr.cpp @@ -62,19 +62,21 @@ namespace Phonon VideoRendererEVR::VideoRendererEVR(QWidget *target) : m_target(target) { + if (QSysInfo::WindowsVersion < QSysInfo::WV_VISTA) + return; m_filter = Filter(CLSID_EnhancedVideoRenderer, IID_IBaseFilter); if (!m_filter) { return; } ComPointer<IMFVideoDisplayControl> filterControl = getService<IMFVideoDisplayControl>(m_filter, MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl); - if (!filterControl) { + if (!filterControl || + FAILED(filterControl->SetVideoWindow(reinterpret_cast<HWND>(target->winId()))) || + FAILED(filterControl->SetAspectRatioMode(MFVideoARMode_None)) || // We're in control of the size + !getService<IMFVideoMixerControl>(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoMixerControl) || + !getService<IMFVideoProcessor>(m_filter, MR_VIDEO_MIXER_SERVICE, IID_IMFVideoProcessor)) { m_filter = Filter(); //will release the interface - return; } - - filterControl->SetVideoWindow(reinterpret_cast<HWND>(target->winId())); - filterControl->SetAspectRatioMode(MFVideoARMode_None); // We're in control of the size } QImage VideoRendererEVR::snapshot() const diff --git a/src/corelib/io/qprocess_symbian.cpp b/src/corelib/io/qprocess_symbian.cpp index 003e781..5b283a5 100644 --- a/src/corelib/io/qprocess_symbian.cpp +++ b/src/corelib/io/qprocess_symbian.cpp @@ -1050,6 +1050,7 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a newProc->Resume(); newProc->Close(); + delete newProc; return true; } diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 82c444e..303b21c 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -436,12 +436,13 @@ void QDeclarativeTextPrivate::invalidateImageCache() { Q_Q(QDeclarativeText); - if (imageCacheDirty) - return; - - imageCacheDirty = true; - imageCache = QPixmap(); + if(cacheAllTextAsImage || style != QDeclarativeText::Normal){//If actually using the image cache + if (imageCacheDirty) + return; + imageCacheDirty = true; + imageCache = QPixmap(); + } if (q->isComponentComplete()) q->update(); } diff --git a/src/declarative/qml/qdeclarativedata_p.h b/src/declarative/qml/qdeclarativedata_p.h index def4188..4767169 100644 --- a/src/declarative/qml/qdeclarativedata_p.h +++ b/src/declarative/qml/qdeclarativedata_p.h @@ -65,6 +65,7 @@ class QDeclarativeContext; class QDeclarativePropertyCache; class QDeclarativeContextData; class QDeclarativeNotifier; +class QDeclarativeDataExtended; // This class is structured in such a way, that simply zero'ing it is the // default state for elemental object allocations. This is crucial in the // workings of the QDeclarativeInstruction::CreateSimpleObject instruction. @@ -150,17 +151,13 @@ public: } } + bool hasExtendedData() const { return extendedData != 0; } QDeclarativeNotifier *objectNameNotifier() const; QHash<int, QObject *> *attachedProperties() const; - struct ExtendedData { - ExtendedData(); - ~ExtendedData(); - - QHash<int, QObject *> attachedProperties; - void *objectNameNotifier; - }; - mutable ExtendedData *extendedData; +private: + // For objectNameNotifier and attachedProperties + mutable QDeclarativeDataExtended *extendedData; }; template<class T> diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index 808ba68..1160ed8 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -957,7 +957,7 @@ QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool cre if (!data) return 0; // Attached properties are only on objects created by QML - QObject *rv = data->extendedData?data->attachedProperties()->value(id):0; + QObject *rv = data->hasExtendedData()?data->attachedProperties()->value(id):0; if (rv || !create) return rv; @@ -985,6 +985,35 @@ QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, return qmlAttachedPropertiesObjectById(*idCache, object, create); } +class QDeclarativeDataExtended { +public: + QDeclarativeDataExtended(); + ~QDeclarativeDataExtended(); + + QHash<int, QObject *> attachedProperties; + QDeclarativeNotifier objectNameNotifier; +}; + +QDeclarativeDataExtended::QDeclarativeDataExtended() +{ +} + +QDeclarativeDataExtended::~QDeclarativeDataExtended() +{ +} + +QDeclarativeNotifier *QDeclarativeData::objectNameNotifier() const +{ + if (!extendedData) extendedData = new QDeclarativeDataExtended; + return &extendedData->objectNameNotifier; +} + +QHash<int, QObject *> *QDeclarativeData::attachedProperties() const +{ + if (!extendedData) extendedData = new QDeclarativeDataExtended; + return &extendedData->attachedProperties; +} + void QDeclarativeData::destroyed(QObject *object) { if (deferredComponent) @@ -1075,28 +1104,6 @@ void QDeclarativeData::setBindingBit(QObject *obj, int bit) bindingBits[bit / 32] |= (1 << (bit % 32)); } -QDeclarativeData::ExtendedData::ExtendedData() -: objectNameNotifier(0) -{ -} - -QDeclarativeData::ExtendedData::~ExtendedData() -{ - ((QDeclarativeNotifier *)&objectNameNotifier)->~QDeclarativeNotifier(); -} - -QDeclarativeNotifier *QDeclarativeData::objectNameNotifier() const -{ - if (!extendedData) extendedData = new ExtendedData; - return (QDeclarativeNotifier *)&extendedData->objectNameNotifier; -} - -QHash<int, QObject *> *QDeclarativeData::attachedProperties() const -{ - if (!extendedData) extendedData = new ExtendedData; - return &extendedData->attachedProperties; -} - /*! Creates a QScriptValue allowing you to use \a object in QML script. \a engine is the QDeclarativeEngine it is to be created in. diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 4d27531..0531595 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -327,8 +327,6 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket Q_ASSERT(socket); Q_ASSERT(reply); - Q_Q(QHttpNetworkConnection); - resend = false; //create the response header to be used with QAuthenticatorPrivate. QList<QPair<QByteArray, QByteArray> > fields = reply->header(); @@ -854,12 +852,17 @@ QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest return d->queueRequest(request); } -bool QHttpNetworkConnection::isEncrypted() const +bool QHttpNetworkConnection::isSsl() const { Q_D(const QHttpNetworkConnection); return d->encrypt; } +QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const +{ + return d_func()->channels; +} + #ifndef QT_NO_NETWORKPROXY void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy) { diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 8461426c..9f23cbf 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -108,7 +108,9 @@ public: QNetworkProxy transparentProxy() const; #endif - bool isEncrypted() const; + bool isSsl() const; + + QHttpNetworkConnectionChannel *channels() const; #ifndef QT_NO_OPENSSL void setSslConfiguration(const QSslConfiguration &config); diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 02daa50..c8caad4 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -180,7 +180,6 @@ bool QHttpNetworkConnectionChannel::sendRequest() reply->d_func()->autoDecompress = request.d->autoDecompress; reply->d_func()->pipeliningUsed = false; - pendingEncrypt = false; // if the url contains authentication parameters, use the new ones // both channels will use the new authentication parameters if (!request.url().userInfo().isEmpty() && request.withCredentials()) { @@ -1019,6 +1018,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted() if (!socket) return; // ### error state = QHttpNetworkConnectionChannel::IdleState; + pendingEncrypt = false; sendRequest(); } diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index 442086a..fd18042 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -158,6 +158,8 @@ public: bool isSocketWaiting() const; bool isSocketReading() const; + friend class QNetworkAccessHttpBackend; + protected slots: void _q_receiveReply(); void _q_bytesWritten(qint64 bytes); // proceed sending diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index e4eb7c4..21bc427 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -188,6 +188,12 @@ QByteArray QHttpNetworkReply::readAny() return d->responseData.read(); } +QByteArray QHttpNetworkReply::readAll() +{ + Q_D(QHttpNetworkReply); + return d->responseData.readAll(); +} + void QHttpNetworkReply::setDownstreamLimited(bool dsl) { Q_D(QHttpNetworkReply); diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 3f79d81..9cf805c 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -126,6 +126,7 @@ public: qint64 bytesAvailable() const; qint64 bytesAvailableNextBlock() const; QByteArray readAny(); + QByteArray readAll(); void setDownstreamLimited(bool t); bool isFinished() const; diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp index 05eb6cb..12b6400 100644 --- a/src/network/access/qnetworkaccessbackend.cpp +++ b/src/network/access/qnetworkaccessbackend.cpp @@ -142,6 +142,7 @@ void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 byt QNetworkAccessBackend::QNetworkAccessBackend() : manager(0) , reply(0) + , synchronous(false) { } diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h index 7faa5cb..c9ec37e 100644 --- a/src/network/access/qnetworkaccessbackend_p.h +++ b/src/network/access/qnetworkaccessbackend_p.h @@ -157,6 +157,9 @@ public: QVariant attribute(QNetworkRequest::Attribute code) const; void setAttribute(QNetworkRequest::Attribute code, const QVariant &value); + bool isSynchronous() { return synchronous; } + void setSynchronous(bool sync) { synchronous = sync; } + // return true if the QNonContiguousByteDevice of the upload // data needs to support reset(). Currently needed for HTTP. // This will possibly enable buffering of the upload data. @@ -166,6 +169,8 @@ public: virtual bool canResume() const { return false; } virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); } + virtual bool processRequestSynchronously() { return false; } + protected: // Create the device used for reading the upload data QNonContiguousByteDevice* createUploadByteDevice(); @@ -200,6 +205,7 @@ private: friend class QNetworkReplyImplPrivate; QNetworkAccessManagerPrivate *manager; QNetworkReplyImplPrivate *reply; + bool synchronous; }; class QNetworkAccessBackendFactory diff --git a/src/network/access/qnetworkaccessdatabackend.cpp b/src/network/access/qnetworkaccessdatabackend.cpp index efb6e3e..74aebdb 100644 --- a/src/network/access/qnetworkaccessdatabackend.cpp +++ b/src/network/access/qnetworkaccessdatabackend.cpp @@ -122,4 +122,10 @@ bool QNetworkAccessDataBackend::waitForUpstreamBytesWritten(int) return false; } +bool QNetworkAccessDataBackend::processRequestSynchronously() +{ + start(); + return true; +} + QT_END_NAMESPACE diff --git a/src/network/access/qnetworkaccessdatabackend_p.h b/src/network/access/qnetworkaccessdatabackend_p.h index a7c63d5..0e5a494 100644 --- a/src/network/access/qnetworkaccessdatabackend_p.h +++ b/src/network/access/qnetworkaccessdatabackend_p.h @@ -68,6 +68,8 @@ public: virtual void closeUpstreamChannel(); virtual bool waitForDownstreamReadyRead(int msecs); virtual bool waitForUpstreamBytesWritten(int msecs); + + virtual bool processRequestSynchronously(); }; class QNetworkAccessDataBackendFactory: public QNetworkAccessBackendFactory diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp index 80b05a4..9df5d7b 100644 --- a/src/network/access/qnetworkaccesshttpbackend.cpp +++ b/src/network/access/qnetworkaccesshttpbackend.cpp @@ -50,6 +50,7 @@ #include "qnetworkrequest_p.h" #include "qnetworkcookie_p.h" #include "QtCore/qdatetime.h" +#include "QtCore/qelapsedtimer.h" #include "QtNetwork/qsslconfiguration.h" #ifndef QT_NO_HTTP @@ -318,7 +319,10 @@ void QNetworkAccessHttpBackend::disconnectFromHttp() // Get the object cache that stores our QHttpNetworkConnection objects QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this); - cache->releaseEntry(cacheKey); + + // synchronous calls are not put into the cache, so for them the key is empty + if (!cacheKey.isEmpty()) + cache->releaseEntry(cacheKey); } // This is abut disconnecting signals, not about disconnecting TCP connections @@ -639,34 +643,49 @@ void QNetworkAccessHttpBackend::open() if (transparentProxy.type() == QNetworkProxy::DefaultProxy && cacheProxy.type() == QNetworkProxy::DefaultProxy) { // unsuitable proxies - QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, - Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), - Q_ARG(QString, tr("No suitable proxy found"))); - QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); - return; + if (isSynchronous()) { + error(QNetworkReply::ProxyNotFoundError, tr("No suitable proxy found")); + finished(); + } else { + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError), + Q_ARG(QString, tr("No suitable proxy found"))); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + } + return; } #endif - // check if we have an open connection to this host - cacheKey = makeCacheKey(this, theProxy); - QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this); - // the http object is actually a QHttpNetworkConnection - http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey)); - if (http == 0) { - // no entry in cache; create an object - // the http object is actually a QHttpNetworkConnection - http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt); - + if (isSynchronous()) { + // for synchronous requests, we just create a new connection + http = new QHttpNetworkConnection(1, url.host(), url.port(), encrypt, this); #ifndef QT_NO_NETWORKPROXY http->setTransparentProxy(transparentProxy); http->setCacheProxy(cacheProxy); #endif + postRequest(); + processRequestSynchronously(); + } else { + // check if we have an open connection to this host + cacheKey = makeCacheKey(this, theProxy); + QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this); + // the http object is actually a QHttpNetworkConnection + http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey)); + if (http == 0) { + // no entry in cache; create an object + // the http object is actually a QHttpNetworkConnection + http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt); - // cache the QHttpNetworkConnection corresponding to this cache key - cache->addEntry(cacheKey, http); - } +#ifndef QT_NO_NETWORKPROXY + http->setTransparentProxy(transparentProxy); + http->setCacheProxy(cacheProxy); +#endif - postRequest(); + // cache the QHttpNetworkConnection corresponding to this cache key + cache->addEntry(cacheKey, static_cast<QNetworkAccessCachedHttpConnection *>(http.data())); + } + postRequest(); + } } void QNetworkAccessHttpBackend::closeDownstreamChannel() @@ -1125,6 +1144,87 @@ void QNetworkAccessHttpBackend::setResumeOffset(quint64 offset) resumeOffset = offset; } +bool QNetworkAccessHttpBackend::processRequestSynchronously() +{ + QHttpNetworkConnectionChannel *channel = &http->channels()[0]; + + // Disconnect all socket signals. They will only confuse us when using waitFor* + QObject::disconnect(channel->socket, 0, 0, 0); + + qint64 timeout = 20*1000; // 20 sec + QElapsedTimer timeoutTimer; + + bool waitResult = channel->socket->waitForConnected(timeout); + timeoutTimer.start(); + + if (!waitResult || channel->socket->state() != QAbstractSocket::ConnectedState) { + error(QNetworkReply::UnknownNetworkError, QLatin1String("could not connect")); + return false; + } + channel->_q_connected(); // this will send the request (via sendRequest()) + +#ifndef QT_NO_OPENSSL + if (http->isSsl()) { + qint64 remainingTimeEncrypted = timeout - timeoutTimer.elapsed(); + if (!static_cast<QSslSocket *>(channel->socket)->waitForEncrypted(remainingTimeEncrypted)) { + error(QNetworkReply::SslHandshakeFailedError, + QLatin1String("could not encrypt or timeout while encrypting")); + return false; + } + channel->_q_encrypted(); + } +#endif + + // if we get a 401 or 407, we might need to send the request twice, see below + bool authenticating = false; + + do { + channel->sendRequest(); + + qint64 remainingTimeBytesWritten; + while(channel->socket->bytesToWrite() > 0 || + channel->state == QHttpNetworkConnectionChannel::WritingState) { + remainingTimeBytesWritten = timeout - timeoutTimer.elapsed(); + channel->sendRequest(); // triggers channel->socket->write() + if (!channel->socket->waitForBytesWritten(remainingTimeBytesWritten)) { + error(QNetworkReply::TimeoutError, + QLatin1String("could not write bytes to socket or timeout while writing")); + return false; + } + } + + qint64 remainingTimeBytesRead = timeout - timeoutTimer.elapsed(); + // Loop for at most remainingTime until either the socket disconnects + // or the reply is finished + do { + waitResult = channel->socket->waitForReadyRead(remainingTimeBytesRead); + remainingTimeBytesRead = timeout - timeoutTimer.elapsed(); + if (!waitResult || remainingTimeBytesRead <= 0 + || channel->socket->state() != QAbstractSocket::ConnectedState) { + error(QNetworkReply::TimeoutError, + QLatin1String("could not read from socket or timeout while reading")); + return false; + } + + if (channel->socket->bytesAvailable()) + channel->_q_readyRead(); + + if (!httpReply) + return false; // we got a 401 or 407 and cannot handle it (it might happen that + // disconnectFromHttp() was called, in that case the reply is zero) + // ### I am quite sure this does not work for NTLM + // ### how about uploading to an auth / proxyAuth site? + + authenticating = (httpReply->statusCode() == 401 || httpReply->statusCode() == 407); + + if (httpReply->isFinished()) + break; + } while (remainingTimeBytesRead > 0); + } while (authenticating); + + return true; +} + QT_END_NAMESPACE #endif // QT_NO_HTTP diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h index 5de3429..cc2f9ac 100644 --- a/src/network/access/qnetworkaccesshttpbackend_p.h +++ b/src/network/access/qnetworkaccesshttpbackend_p.h @@ -99,6 +99,8 @@ public: bool canResume() const; void setResumeOffset(quint64 offset); + virtual bool processRequestSynchronously(); + private slots: void replyReadyRead(); void replyFinished(); @@ -111,7 +113,7 @@ private slots: private: QHttpNetworkReply *httpReply; - QPointer<QNetworkAccessCachedHttpConnection> http; + QPointer<QHttpNetworkConnection> http; QByteArray cacheKey; QNetworkAccessBackendUploadIODevice *uploadDevice; diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp index effd79e..4bc036e 100644 --- a/src/network/access/qnetworkaccessmanager.cpp +++ b/src/network/access/qnetworkaccessmanager.cpp @@ -1060,12 +1060,14 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera priv->backend->setParent(reply); priv->backend->reply = priv; } - // fourth step: setup the reply - priv->setup(op, request, outgoingData); #ifndef QT_NO_OPENSSL reply->setSslConfiguration(request.sslConfiguration()); #endif + + // fourth step: setup the reply + priv->setup(op, request, outgoingData); + return reply; } @@ -1141,6 +1143,11 @@ void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend } } + // if we emit a signal here in synchronous mode, the user might spin + // an event loop, which might recurse and lead to problems + if (backend->isSynchronous()) + return; + backend->reply->urlForLastAuthentication = url; emit q->authenticationRequired(backend->reply->q_func(), authenticator); cacheCredentials(url, authenticator); @@ -1168,6 +1175,11 @@ void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBac } } + // if we emit a signal here in synchronous mode, the user might spin + // an event loop, which might recurse and lead to problems + if (backend->isSynchronous()) + return; + backend->reply->lastProxyAuthentication = proxy; emit q->proxyAuthenticationRequired(proxy, authenticator); cacheProxyCredentials(proxy, authenticator); diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp index 5850494..cf6e674 100644 --- a/src/network/access/qnetworkreplyimpl.cpp +++ b/src/network/access/qnetworkreplyimpl.cpp @@ -85,7 +85,7 @@ void QNetworkReplyImplPrivate::_q_startOperation() } #ifndef QT_NO_BEARERMANAGEMENT - if (!backend->start()) { + if (!backend->start()) { // ### we should call that method even if bearer is not used // backend failed to start because the session state is not Connected. // QNetworkAccessManager will call reply->backend->start() again for us when the session // state changes. @@ -109,11 +109,15 @@ void QNetworkReplyImplPrivate::_q_startOperation() } #endif - if (state != Finished) { - if (operation == QNetworkAccessManager::GetOperation) - pendingNotifications.append(NotifyDownstreamReadyWrite); + if (backend->isSynchronous()) { + state = Finished; + } else { + if (state != Finished) { + if (operation == QNetworkAccessManager::GetOperation) + pendingNotifications.append(NotifyDownstreamReadyWrite); - handleNotifications(); + handleNotifications(); + } } } @@ -287,7 +291,25 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const url = request.url(); operation = op; - if (outgoingData && backend) { + q->QIODevice::open(QIODevice::ReadOnly); + // Internal code that does a HTTP reply for the synchronous Ajax + // in QtWebKit. + QVariant synchronousHttpAttribute = req.attribute( + static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1)); + if (synchronousHttpAttribute.toBool()) { + backend->setSynchronous(true); + if (outgoingData && outgoingData->isSequential()) { + outgoingDataBuffer = new QRingBuffer(); + QByteArray data; + do { + data = outgoingData->readAll(); + if (data.isEmpty()) + break; + outgoingDataBuffer->append(data); + } while (1); + } + } + if (outgoingData && backend && !backend->isSynchronous()) { // there is data to be uploaded, e.g. HTTP POST. if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) { @@ -298,7 +320,7 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const } else { bool bufferingDisallowed = req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute, - false).toBool(); + false).toBool(); if (bufferingDisallowed) { // if a valid content-length header for the request was supplied, we can disable buffering @@ -323,17 +345,18 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const // for HTTP, we want to send out the request as fast as possible to the network, without // invoking methods in a QueuedConnection #ifndef QT_NO_HTTP - if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) { + if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) { _q_startOperation(); } else { QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); } #else - QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); + if (backend->isSynchronous()) + _q_startOperation(); + else + QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection); #endif // QT_NO_HTTP - } - - q->QIODevice::open(QIODevice::ReadOnly); + } } void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification) diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h index cdadf0f..9bcc900 100644 --- a/src/network/access/qnetworkrequest.h +++ b/src/network/access/qnetworkrequest.h @@ -85,6 +85,9 @@ public: MaximumDownloadBufferSizeAttribute, // internal DownloadBufferAttribute, // internal + // (DownloadBufferAttribute + 1) is reserved internal for QSynchronousHttpNetworkReply + // add the enum in 4.8 + User = 1000, UserMax = 32767 }; diff --git a/tests/auto/declarative/qmlvisual/tst_qmlvisual.cpp b/tests/auto/declarative/qmlvisual/tst_qmlvisual.cpp index 18fbfca..ef0d4dc 100644 --- a/tests/auto/declarative/qmlvisual/tst_qmlvisual.cpp +++ b/tests/auto/declarative/qmlvisual/tst_qmlvisual.cpp @@ -147,9 +147,11 @@ void tst_qmlvisual::visual() QProcess p; p.start(qmlruntime, arguments); - QVERIFY(p.waitForFinished()); + bool finished = p.waitForFinished(); + QByteArray output = p.readAllStandardOutput() + p.readAllStandardError(); + QVERIFY2(finished, output.data()); if (p.exitCode() != 0) - qDebug() << p.readAllStandardError(); + qDebug() << output; QCOMPARE(p.exitStatus(), QProcess::NormalExit); QCOMPARE(p.exitCode(), 0); } diff --git a/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp b/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp index 04bd432..fc8a126 100644 --- a/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp +++ b/tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp @@ -58,20 +58,29 @@ public: private slots: void expires_data(); void expires(); + void expiresSynchronous_data(); + void expiresSynchronous(); void lastModified_data(); void lastModified(); + void lastModifiedSynchronous_data(); + void lastModifiedSynchronous(); void etag_data(); void etag(); + void etagSynchronous_data(); + void etagSynchronous(); void cacheControl_data(); void cacheControl(); + void cacheControlSynchronous_data(); + void cacheControlSynchronous(); void deleteCache(); private: void check(); + void checkSynchronous(); }; class NetworkDiskCache : public QNetworkDiskCache @@ -142,6 +151,16 @@ void tst_QAbstractNetworkCache::expires() check(); } +void tst_QAbstractNetworkCache::expiresSynchronous_data() +{ + expires_data(); +} + +void tst_QAbstractNetworkCache::expiresSynchronous() +{ + checkSynchronous(); +} + void tst_QAbstractNetworkCache::lastModified_data() { QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl"); @@ -164,6 +183,16 @@ void tst_QAbstractNetworkCache::lastModified() check(); } +void tst_QAbstractNetworkCache::lastModifiedSynchronous_data() +{ + tst_QAbstractNetworkCache::lastModified_data(); +} + +void tst_QAbstractNetworkCache::lastModifiedSynchronous() +{ + checkSynchronous(); +} + void tst_QAbstractNetworkCache::etag_data() { QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl"); @@ -186,6 +215,16 @@ void tst_QAbstractNetworkCache::etag() check(); } +void tst_QAbstractNetworkCache::etagSynchronous_data() +{ + tst_QAbstractNetworkCache::etag_data(); +} + +void tst_QAbstractNetworkCache::etagSynchronous() +{ + checkSynchronous(); +} + void tst_QAbstractNetworkCache::cacheControl_data() { QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl"); @@ -217,6 +256,16 @@ void tst_QAbstractNetworkCache::cacheControl() check(); } +void tst_QAbstractNetworkCache::cacheControlSynchronous_data() +{ + tst_QAbstractNetworkCache::cacheControl_data(); +} + +void tst_QAbstractNetworkCache::cacheControlSynchronous() +{ + checkSynchronous(); +} + void tst_QAbstractNetworkCache::check() { QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl); @@ -250,8 +299,6 @@ void tst_QAbstractNetworkCache::check() QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError); QCOMPARE(secondData, QByteArray()); } else { - if (reply2->error() != QNetworkReply::NoError) - qDebug() << reply2->errorString(); QCOMPARE(reply2->error(), QNetworkReply::NoError); QCOMPARE(QString(secondData), QString(goodData)); QCOMPARE(secondData, goodData); @@ -263,16 +310,60 @@ void tst_QAbstractNetworkCache::check() QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList(); qSort(rawHeaderList); qSort(rawHeaderList2); + } + QCOMPARE(diskCache->gotData, fetchFromCache); +} - // headers can change - for (int i = 0; i < rawHeaderList.count(); ++i) { - //qDebug() << i << rawHeaderList.value(i) << reply->rawHeader(rawHeaderList.value(i)); - //qDebug() << i << rawHeaderList2.value(i) << reply2->rawHeader(rawHeaderList2.value(i)); - //QCOMPARE(QString(rawHeaderList.value(i)), QString(rawHeaderList2.value(i))); - //QCOMPARE(QString(reply->rawHeader(rawHeaderList.value(i))), QString(reply2->rawHeader(rawHeaderList2.value(i)))); - } - //QCOMPARE(rawHeaderList.count(), rawHeaderList2.count()); +void tst_QAbstractNetworkCache::checkSynchronous() +{ + QSKIP("not working yet, see QTBUG-15221", SkipAll); + QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl); + QFETCH(QString, url); + QFETCH(bool, fetchFromCache); + + QNetworkAccessManager manager; + NetworkDiskCache *diskCache = new NetworkDiskCache(&manager); + manager.setCache(diskCache); + QCOMPARE(diskCache->gotData, false); + + QUrl realUrl = url.contains("://") ? url : TESTFILE + url; + QNetworkRequest request(realUrl); + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1), + true); + + // prime the cache + QNetworkReply *reply = manager.get(request); + QVERIFY(reply->isFinished()); // synchronous + QCOMPARE(diskCache->gotData, false); + QByteArray goodData = reply->readAll(); + + request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl); + + // should be in the cache now + QNetworkReply *reply2 = manager.get(request); + QVERIFY(reply2->isFinished()); // synchronous + + QByteArray secondData = reply2->readAll(); + if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) { + QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError); + QCOMPARE(secondData, QByteArray()); + } else { + if (reply2->error() != QNetworkReply::NoError) + qDebug() << reply2->errorString(); + QCOMPARE(reply2->error(), QNetworkReply::NoError); + QCOMPARE(QString(secondData), QString(goodData)); + QCOMPARE(secondData, goodData); + QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + } + + if (fetchFromCache) { + QList<QByteArray> rawHeaderList = reply->rawHeaderList(); + QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList(); + qSort(rawHeaderList); + qSort(rawHeaderList2); } QCOMPARE(diskCache->gotData, fetchFromCache); } diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index ddb7687..90416f2 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -75,7 +75,6 @@ #include "../network-settings.h" - Q_DECLARE_METATYPE(QNetworkReply*) Q_DECLARE_METATYPE(QAuthenticator*) Q_DECLARE_METATYPE(QNetworkProxy) @@ -84,6 +83,8 @@ Q_DECLARE_METATYPE(QList<QNetworkProxy>) Q_DECLARE_METATYPE(QNetworkReply::NetworkError) Q_DECLARE_METATYPE(QBuffer*) +const int SynchronousRequestAttribute = QNetworkRequest::DownloadBufferAttribute + 1; + class QNetworkReplyPtr: public QSharedPointer<QNetworkReply> { public: @@ -108,6 +109,16 @@ class tst_QNetworkReply: public QObject bool requiresAuthentication; }; + static bool seedCreated; + static QString createUniqueExtension() { + if (!seedCreated) { + qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) + QCoreApplication::applicationPid()); + seedCreated = true; // not thread-safe, but who cares + } + QString s = QString("%1-%2-%3").arg(QTime(0,0,0).msecsTo(QTime::currentTime())).arg(QCoreApplication::applicationPid()).arg(qrand()); + return s; + }; + QEventLoop *loop; enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; int returnCode; @@ -173,8 +184,12 @@ private Q_SLOTS: void putToFtp(); void putToHttp_data(); void putToHttp(); + void putToHttpSynchronous_data(); + void putToHttpSynchronous(); void postToHttp_data(); void postToHttp(); + void postToHttpSynchronous_data(); + void postToHttpSynchronous(); void deleteFromHttp_data(); void deleteFromHttp(); void putGetDeleteGetFromHttp_data(); @@ -198,7 +213,9 @@ private Q_SLOTS: void ioGetFromHttpWithReuseParallel(); void ioGetFromHttpWithReuseSequential(); void ioGetFromHttpWithAuth(); + void ioGetFromHttpWithAuthSynchronous(); void ioGetFromHttpWithProxyAuth(); + void ioGetFromHttpWithProxyAuthSynchronous(); void ioGetFromHttpWithSocksProxy(); #ifndef QT_NO_OPENSSL void ioGetFromHttpsWithSslErrors(); @@ -233,6 +250,8 @@ private Q_SLOTS: void ioPostToHttpFromFile(); void ioPostToHttpFromSocket_data(); void ioPostToHttpFromSocket(); + void ioPostToHttpFromSocketSynchronous(); + void ioPostToHttpFromSocketSynchronous_data(); void ioPostToHttpFromMiddleOfFileToEnd(); void ioPostToHttpFromMiddleOfFileFiveBytes(); void ioPostToHttpFromMiddleOfQBufferFiveBytes(); @@ -258,13 +277,19 @@ private Q_SLOTS: void receiveCookiesFromHttp_data(); void receiveCookiesFromHttp(); + void receiveCookiesFromHttpSynchronous_data(); + void receiveCookiesFromHttpSynchronous(); void sendCookies_data(); void sendCookies(); + void sendCookiesSynchronous_data(); + void sendCookiesSynchronous(); void nestedEventLoops(); void httpProxyCommands_data(); void httpProxyCommands(); + void httpProxyCommandsSynchronous_data(); + void httpProxyCommandsSynchronous(); void proxyChange(); void authorizationError_data(); void authorizationError(); @@ -303,12 +328,18 @@ private Q_SLOTS: void qtbug15311doubleContentLength(); + void synchronousRequest_data(); + void synchronousRequest(); + void synchronousRequestSslFailure(); + // NOTE: This test must be last! void parentingRepliesToTheApp(); }; QT_BEGIN_NAMESPACE +bool tst_QNetworkReply::seedCreated = false; + namespace QTest { template<> char *toString(const QNetworkReply::NetworkError& code) @@ -915,14 +946,15 @@ protected: tst_QNetworkReply::tst_QNetworkReply() { + qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy + qRegisterMetaType<QAuthenticator *>(); + qRegisterMetaType<QNetworkProxy>(); + qRegisterMetaType<QList<QSslError> >(); + Q_SET_DEFAULT_IAP testFileName = QDir::currentPath() + "/testfile"; -#ifndef Q_OS_WINCE - uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)time(0)); -#else - uniqueExtension = QString("%1%2").arg((qulonglong)this).arg(rand()); -#endif + uniqueExtension = createUniqueExtension(); cookieJar = new MyCookieJar; manager.setCookieJar(cookieJar); @@ -1009,15 +1041,25 @@ QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op, Q_ASSERT_X(false, "tst_QNetworkReply", "Invalid/unknown operation requested"); } reply->setParent(this); - connect(reply, SIGNAL(finished()), SLOT(finished())); - connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); returnCode = Timeout; - loop = new QEventLoop; - QTimer::singleShot(20000, loop, SLOT(quit())); - int code = returnCode == Timeout ? loop->exec() : returnCode; - delete loop; - loop = 0; + int code = Success; + + if (request.attribute(static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute)).toBool()) { + if (reply->isFinished()) + code = reply->error() != QNetworkReply::NoError ? Failure : Success; + else + code = Failure; + } else { + connect(reply, SIGNAL(finished()), SLOT(finished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(gotError())); + + loop = new QEventLoop; + QTimer::singleShot(20000, loop, SLOT(quit())); + code = returnCode == Timeout ? loop->exec() : returnCode; + delete loop; + loop = 0; + } switch (code) { case Failure: @@ -1492,6 +1534,9 @@ void tst_QNetworkReply::putToFile_data() data = QByteArray(128*1024+1, '\177'); QTest::newRow("128k+1") << data << md5sum(data); + + data = QByteArray(2*1024*1024+1, '\177'); + QTest::newRow("2MB+1") << data << md5sum(data); } void tst_QNetworkReply::putToFile() @@ -1598,6 +1643,47 @@ void tst_QNetworkReply::putToHttp() QCOMPARE(uploadedData, data); } +void tst_QNetworkReply::putToHttpSynchronous_data() +{ + uniqueExtension = createUniqueExtension(); + putToFile_data(); +} + +void tst_QNetworkReply::putToHttpSynchronous() +{ + QUrl url("http://" + QtNetworkSettings::serverName()); + url.setPath(QString("/dav/qnetworkaccess-putToHttp-%1-%2") + .arg(QTest::currentDataTag()) + .arg(uniqueExtension)); + + QNetworkRequest request(url); + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PutOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 201); // 201 Created + + // download the file again from HTTP to make sure it was uploaded + // correctly. HTTP/0.9 is enough + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 80); + socket.write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority) + "\r\n"); + if (!socket.waitForDisconnected(10000)) + QFAIL("Network timeout"); + + QByteArray uploadedData = socket.readAll(); + QCOMPARE(uploadedData, data); +} + void tst_QNetworkReply::postToHttp_data() { putToFile_data(); @@ -1624,6 +1710,37 @@ void tst_QNetworkReply::postToHttp() QCOMPARE(uploadedData, md5sum.toHex()); } +void tst_QNetworkReply::postToHttpSynchronous_data() +{ + putToFile_data(); +} + +void tst_QNetworkReply::postToHttpSynchronous() +{ + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + + QNetworkRequest request(url); + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply; + + QFETCH(QByteArray, data); + + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QFETCH(QByteArray, md5sum); + QByteArray uploadedData = reply->readAll().trimmed(); + QCOMPARE(uploadedData, md5sum.toHex()); +} + void tst_QNetworkReply::deleteFromHttp_data() { QTest::addColumn<QUrl>("url"); @@ -2048,9 +2165,6 @@ void tst_QNetworkReply::ioGetFromHttpWithReuseSequential() void tst_QNetworkReply::ioGetFromHttpWithAuth() { - qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy - qRegisterMetaType<QAuthenticator *>(); - // This test sends three requests // The first two in parallel // The third after the first two finished @@ -2109,6 +2223,44 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth() QCOMPARE(authspy.count(), 0); } + + // now check with synchronous calls: + reference.seek(0); + { + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(authspy.count(), 0); + + // we cannot use a data reader here, since that connects to the readyRead signal, + // just use readAll() + + // the only thing we check here is that the auth cache was used when using synchronous requests + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(replySync->readAll(), reference.readAll()); + } +} + +void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous() +{ + // verify that we do not enter an endless loop with synchronous calls and wrong credentials + // the case when we succed with the login is tested in ioGetFromHttpWithAuth() + + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfcs-auth/rfc3252.txt")); + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QSignalSpy authspy(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError); + QCOMPARE(authspy.count(), 0); + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401); } void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() @@ -2180,6 +2332,47 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth() QCOMPARE(authspy.count(), 0); } + + // now check with synchronous calls: + reference.seek(0); + { + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(authspy.count(), 0); + + // we cannot use a data reader here, since that connects to the readyRead signal, + // just use readAll() + + // the only thing we check here is that the proxy auth cache was used when using synchronous requests + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(replySync->readAll(), reference.readAll()); + } +} + +void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous() +{ + // verify that we do not enter an endless loop with synchronous calls and wrong credentials + // the case when we succed with the login is tested in ioGetFromHttpWithAuth() + + QNetworkProxy proxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QNetworkRequest request(QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt")); + manager.setProxy(proxy); + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + QNetworkReplyPtr replySync = manager.get(request); + manager.setProxy(QNetworkProxy()); // reset + QVERIFY(replySync->isFinished()); // synchronous + QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError); + QCOMPARE(authspy.count(), 0); + QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407); } void tst_QNetworkReply::ioGetFromHttpWithSocksProxy() @@ -3238,7 +3431,67 @@ void tst_QNetworkReply::ioPostToHttpFromSocket() QTEST(authenticationRequiredSpy.count(), "authenticationRequiredCount"); QTEST(proxyAuthenticationRequiredSpy.count(), "proxyAuthenticationRequiredCount"); - } +} + +void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data() +{ + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QByteArray>("md5sum"); + + QByteArray data; + data = ""; + QTest::newRow("empty") << data << md5sum(data); + + data = "This is a normal message."; + QTest::newRow("generic") << data << md5sum(data); + + data = "This is a message to show that Qt rocks!\r\n\n"; + QTest::newRow("small") << data << md5sum(data); + + data = QByteArray("abcd\0\1\2\abcd",12); + QTest::newRow("with-nul") << data << md5sum(data); + + data = QByteArray(4097, '\4'); + QTest::newRow("4k+1") << data << md5sum(data); + + data = QByteArray(128*1024+1, '\177'); + QTest::newRow("128k+1") << data << md5sum(data); + + data = QByteArray(2*1024*1024+1, '\177'); + QTest::newRow("2MB+1") << data << md5sum(data); +} + +void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous() +{ + QFETCH(QByteArray, data); + + SocketPair socketpair; + QVERIFY(socketpair.create()); + QVERIFY(socketpair.endPoints[0] && socketpair.endPoints[1]); + socketpair.endPoints[0]->write(data); + socketpair.endPoints[0]->waitForBytesWritten(5000); + // ### for 4.8: make the socket pair unbuffered, to not read everything in one go in QNetworkReplyImplPrivate::setup() + QTestEventLoop::instance().enterLoop(3); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi"); + QNetworkRequest request(url); + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply = manager.post(request, socketpair.endPoints[1]); + QVERIFY(reply->isFinished()); + socketpair.endPoints[0]->close(); + + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + // verify that the HTTP status code is 200 Ok + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + + QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex()); +} // this tests checks if rewinding the POST-data to some place in the middle // worked. @@ -3981,6 +4234,38 @@ void tst_QNetworkReply::receiveCookiesFromHttp() QTEST(cookieJar->allCookies(), "expectedCookiesInJar"); } +void tst_QNetworkReply::receiveCookiesFromHttpSynchronous_data() +{ + tst_QNetworkReply::receiveCookiesFromHttp_data(); +} + +void tst_QNetworkReply::receiveCookiesFromHttpSynchronous() +{ + QFETCH(QString, cookieString); + + QByteArray data = cookieString.toLatin1() + '\n'; + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/set-cookie.cgi"); + + QNetworkRequest request(url); + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::PostOperation, request, reply, data)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QList<QNetworkCookie> setCookies = + qvariant_cast<QList<QNetworkCookie> >(reply->header(QNetworkRequest::SetCookieHeader)); + QTEST(setCookies, "expectedCookiesFromHttp"); + QTEST(cookieJar->allCookies(), "expectedCookiesInJar"); +} + void tst_QNetworkReply::sendCookies_data() { QTest::addColumn<QList<QNetworkCookie> >("cookiesToSet"); @@ -4041,6 +4326,35 @@ void tst_QNetworkReply::sendCookies() QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString); } +void tst_QNetworkReply::sendCookiesSynchronous_data() +{ + tst_QNetworkReply::sendCookies_data(); +} + +void tst_QNetworkReply::sendCookiesSynchronous() +{ + QFETCH(QString, expectedCookieString); + QFETCH(QList<QNetworkCookie>, cookiesToSet); + cookieJar->setAllCookies(cookiesToSet); + + QUrl url("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/get-cookie.cgi"); + QNetworkRequest request(url); + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply; + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply)); + + QCOMPARE(reply->url(), url); + QCOMPARE(reply->error(), QNetworkReply::NoError); + + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok + + QCOMPARE(QString::fromLatin1(reply->readAll()).trimmed(), expectedCookieString); +} + void tst_QNetworkReply::nestedEventLoops_slot() { QEventLoop subloop; @@ -4144,6 +4458,49 @@ private: int signalCount; }; +void tst_QNetworkReply::httpProxyCommandsSynchronous_data() +{ + httpProxyCommands_data(); +} + +void tst_QNetworkReply::httpProxyCommandsSynchronous() +{ + QFETCH(QUrl, url); + QFETCH(QByteArray, responseToSend); + QFETCH(QString, expectedCommand); + + // when using synchronous commands, we need a different event loop for + // the server thread, because the client is never returning to the + // event loop + MiniHttpServer proxyServer(responseToSend); + QThread serverThread; + proxyServer.moveToThread(&serverThread); + serverThread.start(); + QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer.serverPort()); + + manager.setProxy(proxy); + QNetworkRequest request(url); + + // send synchronous request + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply = manager.get(request); + QVERIFY(reply->isFinished()); // synchronous + manager.setProxy(QNetworkProxy()); + serverThread.quit(); + + //qDebug() << reply->error() << reply->errorString(); + + // we don't really care if the request succeeded + // especially since it won't succeed in the HTTPS case + // so just check that the command was correct + + QString receivedHeader = proxyServer.receivedData.left(expectedCommand.length()); + QCOMPARE(receivedHeader, expectedCommand); +} + void tst_QNetworkReply::proxyChange() { ProxyChangeHelper helper; @@ -4746,7 +5103,122 @@ void tst_QNetworkReply::qtbug15311doubleContentLength() QCOMPARE(reply->readAll(), QByteArray("ABC")); } +void tst_QNetworkReply::synchronousRequest_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QString>("expected"); + QTest::addColumn<bool>("checkContentLength"); + QTest::addColumn<QString>("mimeType"); + + // ### cache, auth, proxies + + QTest::newRow("http") + << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString("text/plain"); + + QTest::newRow("http-gzip") + << QUrl("http://" + QtNetworkSettings::serverName() + "/qtest/deflate/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << false // don't check content length, because it's gzip encoded + // ### we would need to enflate (un-deflate) the file content and compare the sizes + << QString("text/plain"); + +#ifndef QT_NO_OPENSSL + QTest::newRow("https") + << QUrl("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt") + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString("text/plain"); +#endif + + QTest::newRow("data") + << QUrl(QString::fromLatin1("data:text/plain,hello world")) + << QString("data:hello world") + << true // check content length + << QString("text/plain"); + + QTest::newRow("simple-file") + << QUrl(QString::fromLatin1("file:///" SRCDIR "/rfc3252.txt")) + << QString("file:" SRCDIR "/rfc3252.txt") + << true + << QString(); +} + +// FIXME add testcase for failing network etc +void tst_QNetworkReply::synchronousRequest() +{ + QFETCH(QUrl, url); + QFETCH(QString, expected); + QFETCH(bool, checkContentLength); + QFETCH(QString, mimeType); + + QNetworkRequest request(url); + +#ifndef QT_NO_OPENSSL + // workaround for HTTPS requests: add self-signed server cert to list of CA certs, + // since we cannot react to the sslErrors() signal + // to fix this properly we would need to have an ignoreSslErrors() method in the + // QNetworkRequest, see http://bugreports.qt.nokia.com/browse/QTBUG-14774 + if (url.scheme() == "https") { + QSslConfiguration sslConf; + QList<QSslCertificate> certs = QSslCertificate::fromPath(SRCDIR "/certs/qt-test-server-cacert.pem"); + sslConf.setCaCertificates(certs); + request.setSslConfiguration(sslConf); + } +#endif + + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + + QNetworkReplyPtr reply; + QSignalSpy finishedSpy(&manager, SIGNAL(finished(QNetworkReply*))); + QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>))); + RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0)); + QVERIFY(reply->isFinished()); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(sslErrorsSpy.count(), 0); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType); + + QByteArray expectedContent; + + if (expected.startsWith("file:")) { + QString path = expected.mid(5); + QFile file(path); + file.open(QIODevice::ReadOnly); + expectedContent = file.readAll(); + } else if (expected.startsWith("data:")) { + expectedContent = expected.mid(5).toUtf8(); + } + + if (checkContentLength) + QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), qint64(expectedContent.size())); + QCOMPARE(reply->readAll(), expectedContent); + + reply->deleteLater(); +} + +void tst_QNetworkReply::synchronousRequestSslFailure() +{ + // test that SSL won't be accepted with self-signed certificate, + // and that we do not emit the sslError signal (in the manager that is, + // in the reply we don't care) + QUrl url("https://" + QtNetworkSettings::serverName() + "/qtest/rfc3252.txt"); + QNetworkRequest request(url); + request.setAttribute( + static_cast<QNetworkRequest::Attribute>(SynchronousRequestAttribute), + true); + QNetworkReplyPtr reply; + QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply *, const QList<QSslError> &))); + runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0); + QVERIFY(reply->isFinished()); + QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError); + QCOMPARE(sslErrorsSpy.count(), 0); +} // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 9443fee..f4fd7c6 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -3417,7 +3417,7 @@ void Configure::displayConfig() QString webkit = dictionary[ "WEBKIT" ]; if (webkit == "debug") webkit = "yes (debug)"; - cout << "WebKit support.............." << webkit; + cout << "WebKit support.............." << webkit << endl; } cout << "Declarative support........." << dictionary[ "DECLARATIVE" ] << endl; cout << "Declarative debugging......." << dictionary[ "DECLARATIVE_DEBUG" ] << endl; |