Skip to content

Commit a572f3c

Browse files
committed
multipart reply extracted from WCS provider QgsNetworkReplyParser
1 parent f6c6324 commit a572f3c

File tree

4 files changed

+247
-84
lines changed

4 files changed

+247
-84
lines changed

src/core/CMakeLists.txt

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ SET(QGIS_CORE_SRCS
7878
qgsmessageoutput.cpp
7979
qgsmimedatautils.cpp
8080
qgsmessagelog.cpp
81+
qgsnetworkreplyparser.cpp
8182
qgscredentials.cpp
8283
qgsoverlayobject.cpp
8384
qgsowsconnection.cpp
@@ -286,6 +287,7 @@ SET(QGIS_CORE_MOC_HDRS
286287
qgsmaprenderer.h
287288
qgsmessageoutput.h
288289
qgsmessagelog.h
290+
qgsnetworkreplyparser.h
289291
qgscredentials.h
290292
qgspluginlayer.h
291293
qgsproject.h
@@ -375,6 +377,7 @@ SET(QGIS_CORE_HDRS
375377
qgsmaptopixel.h
376378
qgsmessageoutput.h
377379
qgsmimedatautils.h
380+
qgsnetworkreplyparser.h
378381
qgscredentials.h
379382
qgsoverlayobjectpositionmanager.h
380383
qgsowsconnection.h

src/core/qgsnetworkreplyparser.cpp

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/***************************************************************************
2+
qgsnetworkreplyparser.cpp - Multipart QNetworkReply parser
3+
-------------------
4+
begin : 4 January, 2013
5+
copyright : (C) 2013 by Radim Blazek
6+
email : radim dot blazek at gmail.com
7+
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#include "qgslogger.h"
20+
#include "qgsnetworkreplyparser.h"
21+
22+
#include <QNetworkReply>
23+
#include <QObject>
24+
#include <QRegExp>
25+
#include <QString>
26+
#include <QStringList>
27+
28+
QgsNetworkReplyParser::QgsNetworkReplyParser( QNetworkReply *reply )
29+
: mReply(reply)
30+
, mValid(false)
31+
{
32+
if ( !mReply ) return;
33+
34+
// Content type examples:
35+
// multipart/mixed; boundary=wcs
36+
// multipart/mixed; boundary="wcs"\n
37+
if ( !isMultipart( mReply ) )
38+
{
39+
// reply is not multipart, copy body and headers
40+
QMap<QByteArray,QByteArray> headers;
41+
foreach ( QByteArray h, mReply->rawHeaderList() )
42+
{
43+
headers.insert( h, mReply->rawHeader( h ) );
44+
}
45+
mHeaders.append( headers );
46+
mBodies.append( mReply->readAll() );
47+
}
48+
else // multipart
49+
{
50+
QString contentType = mReply->header( QNetworkRequest::ContentTypeHeader ).toString();
51+
QgsDebugMsg( "contentType: " + contentType );
52+
53+
QRegExp re( ".*boundary=\"?([^\"]+)\"?\\s?", Qt::CaseInsensitive );
54+
55+
if ( !re.indexIn( contentType ) == 0 )
56+
{
57+
mError = tr( "Cannot find boundary in multipart content type" );
58+
return;
59+
}
60+
61+
QString boundary = re.cap( 1 );
62+
QgsDebugMsg( "boundary = " + boundary );
63+
boundary = "--" + boundary;
64+
65+
// Lines should be terminated by CRLF ("\r\n") but any new line combination may appear
66+
QByteArray data = mReply->readAll();
67+
int from, to;
68+
from = data.indexOf( boundary.toAscii(), 0 ) + boundary.length() + 1 ;
69+
//QVector<QByteArray> partHeaders;
70+
//QVector<QByteArray> partBodies;
71+
while ( true )
72+
{
73+
to = data.indexOf( boundary.toAscii(), from );
74+
if ( to < 0 )
75+
{
76+
// It may happent that bondary is missing at the end (GeoServer)
77+
// in that case, take everything to th end
78+
if ( data.size() - from > 1 )
79+
{
80+
to = data.size(); // out of range OK
81+
}
82+
else
83+
{
84+
break;
85+
}
86+
}
87+
QgsDebugMsg( QString( "part %1 - %2" ).arg( from ).arg( to ) );
88+
QByteArray part = data.mid( from, to - from );
89+
// Remove possible new line from beginning
90+
while ( part.size() > 0 && ( part.at( 0 ) == '\r' || part.at( 0 ) == '\n' ) )
91+
{
92+
part.remove( 0, 1 );
93+
}
94+
// Split header and data (find empty new line)
95+
// New lines should be CRLF, but we support also CRLFCRLF, LFLF to find empty line
96+
int pos = 0; // body start
97+
while ( pos < part.size() - 1 )
98+
{
99+
if ( part.at( pos ) == '\n' && ( part.at( pos + 1 ) == '\n' || part.at( pos + 1 ) == '\r' ) )
100+
{
101+
if ( part.at( pos + 1 ) == '\r' ) pos++;
102+
pos += 2;
103+
break;
104+
}
105+
pos++;
106+
}
107+
// parse headers
108+
QMap<QByteArray,QByteArray> headersMap;
109+
QByteArray headers = part.left( pos );
110+
QgsDebugMsg( "headers:\n" + headers );
111+
112+
QStringList headerRows = QString( headers ).split( QRegExp( "[\n\r]+" ) );
113+
foreach ( QString row, headerRows )
114+
{
115+
QgsDebugMsg( "row = " + row );
116+
QStringList kv = row.split( ": " );
117+
headersMap.insert( kv.value( 0 ).toAscii(), kv.value( 1 ).toAscii() );
118+
}
119+
mHeaders.append( headersMap );
120+
121+
mBodies.append( part.mid( pos ) );
122+
123+
from = to + boundary.length() + 1;
124+
}
125+
}
126+
mValid = true;
127+
}
128+
129+
bool QgsNetworkReplyParser::isMultipart( QNetworkReply *reply )
130+
{
131+
if ( !reply ) return false;
132+
133+
QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString();
134+
QgsDebugMsg( "contentType: " + contentType );
135+
136+
// Multipart content type examples:
137+
// multipart/mixed; boundary=wcs
138+
// multipart/mixed; boundary="wcs"\n
139+
if ( contentType.startsWith( "multipart/", Qt::CaseInsensitive ) )
140+
{
141+
return true;
142+
}
143+
return false;
144+
}

src/core/qgsnetworkreplyparser.h

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/***************************************************************************
2+
qgsnetworkreplyparser.h - Multipart QNetworkReply parser
3+
-------------------
4+
begin : 4 January, 2013
5+
copyright : (C) 2013 by Radim Blazek
6+
email : radim dot blazek at gmail.com
7+
8+
***************************************************************************/
9+
10+
/***************************************************************************
11+
* *
12+
* This program is free software; you can redistribute it and/or modify *
13+
* it under the terms of the GNU General Public License as published by *
14+
* the Free Software Foundation; either version 2 of the License, or *
15+
* (at your option) any later version. *
16+
* *
17+
***************************************************************************/
18+
19+
#ifndef QGSNETWORKREPLYPARSER_H
20+
#define QGSNETWORKREPLYPARSER_H
21+
22+
#include <QNetworkReply>
23+
24+
/**
25+
\brief Multipart QNetworkReply parser.
26+
27+
It seams that Qt does not have currently support for multipart reply
28+
and it is not even possible to create QNetworkReply from raw data
29+
so we need a class for multipart QNetworkReply parsing.
30+
31+
*/
32+
33+
class CORE_EXPORT QgsNetworkReplyParser : public QObject
34+
{
35+
Q_OBJECT
36+
37+
public:
38+
/** Constructor
39+
* @param reply */
40+
QgsNetworkReplyParser( QNetworkReply *reply );
41+
42+
/** Indicates if successfully parsed
43+
* @return true if successfully parsed */
44+
bool isValid() const { return mValid; }
45+
46+
/** Get number of parts
47+
* @return number of parts */
48+
int parts() const { return mHeaders.size(); }
49+
50+
/** Get part header
51+
* @param part part index
52+
* @return raw header */
53+
QByteArray rawHeader ( int part, const QByteArray & headerName ) const { return mHeaders.value(part).value(headerName); }
54+
55+
/** Get part part body
56+
* @param part part index
57+
* @return part body */
58+
QByteArray body ( int part ) const { return mBodies.value(part); }
59+
60+
/** Parsing error */
61+
QString error() const { return mError; }
62+
63+
/** Test if reply is multipart.
64+
* @return true if reply is multipart */
65+
static bool isMultipart ( QNetworkReply *reply );
66+
67+
private:
68+
QNetworkReply *mReply;
69+
70+
bool mValid;
71+
72+
QString mError;
73+
74+
/* List of header maps */
75+
QList< QMap<QByteArray,QByteArray> > mHeaders;
76+
77+
/* List of part bodies */
78+
QList<QByteArray> mBodies;
79+
};
80+
81+
#endif

0 commit comments

Comments
 (0)