summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Bastian <thierry.bastian@nokia.com>2010-11-26 23:00:13 (GMT)
committerThierry Bastian <thierry.bastian@nokia.com>2010-11-26 23:00:13 (GMT)
commit5e0c4463c8dc8857e9f64a1bdde9a94cd8c39c4d (patch)
treefa6f20deb2970976262d38894298e1bbf76c1433
parent41b455303b83d07cbffbddbee8d3898d6d81f62a (diff)
parente923d66d4999117081ae40092a9dbba72d880810 (diff)
downloadQt-5e0c4463c8dc8857e9f64a1bdde9a94cd8c39c4d.zip
Qt-5e0c4463c8dc8857e9f64a1bdde9a94cd8c39c4d.tar.gz
Qt-5e0c4463c8dc8857e9f64a1bdde9a94cd8c39c4d.tar.bz2
Merge branch '4.7-upstream' into 4.7-water
-rwxr-xr-xbin/createpackage.pl14
-rwxr-xr-xbin/patch_capabilities.pl215
-rw-r--r--doc/src/platforms/symbian-introduction.qdoc1
-rw-r--r--qmake/generators/symbian/symmake_sbsv2.cpp1
-rw-r--r--src/3rdparty/phonon/ds9/videorenderer_evr.cpp12
-rw-r--r--src/corelib/io/qprocess_symbian.cpp1
-rw-r--r--src/declarative/graphicsitems/qdeclarativetext.cpp11
-rw-r--r--src/declarative/qml/qdeclarativedata_p.h13
-rw-r--r--src/declarative/qml/qdeclarativeengine.cpp53
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp9
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h4
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp2
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h2
-rw-r--r--src/network/access/qhttpnetworkreply.cpp6
-rw-r--r--src/network/access/qhttpnetworkreply_p.h1
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp1
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h6
-rw-r--r--src/network/access/qnetworkaccessdatabackend.cpp6
-rw-r--r--src/network/access/qnetworkaccessdatabackend_p.h2
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp140
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h4
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp16
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp47
-rw-r--r--src/network/access/qnetworkrequest.h3
-rw-r--r--tests/auto/declarative/qmlvisual/tst_qmlvisual.cpp6
-rw-r--r--tests/auto/qabstractnetworkcache/tst_qabstractnetworkcache.cpp111
-rw-r--r--tests/auto/qnetworkreply/tst_qnetworkreply.cpp506
-rw-r--r--tools/configure/configureapp.cpp2
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;