Skip to content

Commit

Permalink
Merge pull request #33018 from troopa81/backport-32811-to-release-3_10
Browse files Browse the repository at this point in the history
backport fix wfs cache request
  • Loading branch information
rouault committed Nov 22, 2019
2 parents 588a096 + df1b273 commit 831780b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 5 deletions.
9 changes: 9 additions & 0 deletions src/providers/wfs/qgswfsrequest.cpp
Expand Up @@ -156,6 +156,7 @@ bool QgsWfsRequest::sendGET( const QUrl &url, bool synchronous, bool forceRefres
// * or the owner thread of mReply is currently not doing anything because it's blocked in future.waitForFinished() (if it is the main thread)
connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished, Qt::DirectConnection );
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress, Qt::DirectConnection );
connect( mReply, &QNetworkReply::readyRead, this, &QgsWfsRequest::replyReadyRead, Qt::DirectConnection );

if ( synchronous )
{
Expand Down Expand Up @@ -270,6 +271,7 @@ bool QgsWfsRequest::sendPOST( const QUrl &url, const QString &contentTypeHeader,
}
connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished );
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress );
connect( mReply, &QNetworkReply::readyRead, this, &QgsWfsRequest::replyReadyRead );

QEventLoop loop;
connect( this, &QgsWfsRequest::downloadFinished, &loop, &QEventLoop::quit );
Expand All @@ -288,6 +290,11 @@ void QgsWfsRequest::abort()
}
}

void QgsWfsRequest::replyReadyRead( )
{
mGotNonEmptyResponse = true;
}

void QgsWfsRequest::replyProgress( qint64 bytesReceived, qint64 bytesTotal )
{
QgsDebugMsgLevel( QStringLiteral( "%1 of %2 bytes downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QStringLiteral( "unknown number of" ) : QString::number( bytesTotal ) ), 4 );
Expand Down Expand Up @@ -363,6 +370,8 @@ void QgsWfsRequest::replyFinished()
}
connect( mReply, &QNetworkReply::finished, this, &QgsWfsRequest::replyFinished, Qt::DirectConnection );
connect( mReply, &QNetworkReply::downloadProgress, this, &QgsWfsRequest::replyProgress, Qt::DirectConnection );
connect( mReply, &QNetworkReply::readyRead, this, &QgsWfsRequest::replyReadyRead, Qt::DirectConnection );

return;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/providers/wfs/qgswfsrequest.h
Expand Up @@ -74,6 +74,7 @@ class QgsWfsRequest : public QObject
protected slots:
void replyProgress( qint64, qint64 );
void replyFinished();
void replyReadyRead();
void requestTimedOut( QNetworkReply *reply );

protected:
Expand Down
116 changes: 111 additions & 5 deletions tests/src/python/test_provider_wfs.py
Expand Up @@ -15,6 +15,9 @@
import re
import shutil
import tempfile
import http.server
import threading
import socketserver

# Needed on Qt 5 so that the serialization of XML is consistent among all executions
os.environ['QT_HASH_SEED'] = '1'
Expand Down Expand Up @@ -417,6 +420,11 @@ def tearDownClass(cls):
shutil.rmtree(cls.basetestpath, True)
cls.vl = None # so as to properly close the provider and remove any temporary file

def tearDown(self):
"""Run after each test"""
# clear possible settings modification made during test
QgsSettings().clear()

def testWkbType(self):
"""N/A for WFS provider"""
pass
Expand Down Expand Up @@ -821,11 +829,11 @@ def testWFST10(self):
self.assertTrue(vl.isValid())

self.assertEqual(vl.dataProvider().capabilities(),
QgsVectorDataProvider.AddFeatures
| QgsVectorDataProvider.ChangeAttributeValues
| QgsVectorDataProvider.ChangeGeometries
| QgsVectorDataProvider.DeleteFeatures
| QgsVectorDataProvider.SelectAtId)
QgsVectorDataProvider.AddFeatures |
QgsVectorDataProvider.ChangeAttributeValues |
QgsVectorDataProvider.ChangeGeometries |
QgsVectorDataProvider.DeleteFeatures |
QgsVectorDataProvider.SelectAtId)

(ret, _) = vl.dataProvider().addFeatures([QgsFeature()])
self.assertFalse(ret)
Expand Down Expand Up @@ -4145,6 +4153,104 @@ def testRetryLogicOnExceptionLackOfPrimaryKey(self):
values = [f['INTFIELD'] for f in vl.getFeatures()]
self.assertEqual(values, [1, 2])

def testCacheRead(self):

# setup a clean cache directory
cache_dir = tempfile.mkdtemp()
QgsSettings().setValue("cache/directory", cache_dir)

# don't retry, http server never fails
QgsSettings().setValue('qgis/defaultTileMaxRetry', '0')

responses = []

class SequentialHandler(http.server.SimpleHTTPRequestHandler):

def do_GET(self):
c, response = responses.pop(0)
self.send_response(c)
self.send_header("Content-type", "application/xml")
self.send_header("Content-length", len(response))
self.send_header('Last-Modified', 'Wed, 05 Jun 2019 15:33:27 GMT')
self.end_headers()
self.wfile.write(response.encode('UTF-8'))

httpd = socketserver.TCPServer(('localhost', 0), SequentialHandler)
port = httpd.server_address[1]

responses.append((200,
"""
<WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:ogc="http://www.opengis.net/ogc">
<FeatureTypeList>
<FeatureType>
<Name>my:typename</Name>
<Title>Title</Title>
<Abstract>Abstract</Abstract>
<SRS>EPSG:4326</SRS>
</FeatureType>
</FeatureTypeList>
</WFS_Capabilities>"""))

responses.append((200,
"""
<xsd:schema xmlns:my="http://my" xmlns:gml="http://www.opengis.net/gml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://my">
<xsd:import namespace="http://www.opengis.net/gml"/>
<xsd:complexType name="typenameType">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="INTFIELD" nillable="true" type="xsd:int"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="typename" substitutionGroup="gml:_Feature" type="my:typenameType"/>
</xsd:schema>
"""))

responses.append((200,
"""
<wfs:FeatureCollection
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:gml="http://www.opengis.net/gml"
xmlns:my="http://my">
<gml:featureMember>
<my:typename fid="typename.0">
<my:INTFIELD>1</my:INTFIELD>
</my:typename>
</gml:featureMember>
<gml:featureMember>
<my:typename fid="typename.1">
<my:INTFIELD>2</my:INTFIELD>
</my:typename>
</gml:featureMember>
</wfs:FeatureCollection>"""))

httpd_thread = threading.Thread(target=httpd.serve_forever)
httpd_thread.setDaemon(True)
httpd_thread.start()

vl = QgsVectorLayer("url='http://localhost:{}' typename='my:typename' version='1.0.0'".format(port), 'test', 'WFS')
self.assertTrue(vl.isValid())
self.assertEqual(vl.wkbType(), QgsWkbTypes.NoGeometry)
self.assertEqual(len(vl.fields()), 1)

res = [f['INTFIELD'] for f in vl.getFeatures()]
self.assertEqual(sorted(res), [1, 2])

# next response is empty, cache must be used
responses.append((304, ""))

# Reload
vl.reload()

res = [f['INTFIELD'] for f in vl.getFeatures()]
# self.assertEqual(len(server.errors()), 0, server.errors())
self.assertEqual(sorted(res), [1, 2])

errors = vl.dataProvider().errors()
self.assertEqual(len(errors), 0, errors)


if __name__ == '__main__':
unittest.main()

0 comments on commit 831780b

Please sign in to comment.