Remoto - VFS: VFS_curl.cpp Source File
Remoto - VFS
VFS_curl.cpp
Go to the documentation of this file.
1 
2 #include <QNetworkRequest>
3 
4 #include "VFS.h"
5 #include "VFS_base/VFS_icons.h"
6 
7 #include "VFS_curl.h"
8 
33 VFS_curl::VFS_curl(QString url, bool json, bool poll, int expire, int timeout)
34 : VFS_node()
35 , _url(url)
36 , _json(json)
37 , _poll(poll)
38 , _expire(expire)
39 , _manager(this)
40 , _stale(true)
41 , _valid(false)
42 , _getting(false)
43 , _timeout(timeout)
44 , _request(nullptr)
45 {
46  connect( &_manager, SIGNAL(finished(QNetworkReply*)),
47  this, SLOT(replyFinished(QNetworkReply*)) );
48 
49  //connect( &_manager, SIGNAL(QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error)),
50  // this, SLOT(replyError(&MyClass::slotError);
51 
52  connect( &_manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
53  this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)) );
54 
55  _timer.setSingleShot(true);
56  connect( &_timer, SIGNAL(timeout()),
57  this, SLOT(networkTimeout()) );
58 
59  if (_poll)
60  {
61  //this will create a log entry with the VFS_curl user, which may not be right if subclassed.
62  //this->expire();
63 
64  //done this way instead so we don't get a log entry from VFS_curl when this is subclassed.
65  QTimer::singleShot( 0, this, SLOT(expire()) );
66  }
67 }
68 
70 {
71 }
72 
80 QByteArray VFS_curl::icon()
81 {
82  return VFS_icons::get("stream");
83 }
84 
91 {
92  return _url.toString( QUrl::FullyEncoded );
93 }
94 
101 {
102  return false;
103 }
104 
115 {
117 
118  r->_metadata["type"] = _json ? "json" : "httpdata";
119 
120  if (_stale)
121  {
122  r->_isCallback = true;
123  get(r);
124  }
125  else
126  {
127  if (_valid)
128  {
129  r->_metadata["http"] = _metadata;
130  r->_success = true;
131  }
132  else
133  {
134  r->_reason = _reason;
135  r->_success = false;
136  }
137  }
138 }
139 
154 {
155  if (_stale)
156  {
157  r->_isCallback = true;
158  get(r);
159  }
160  else
161  {
162  if (_valid)
163  {
164  r->_data.setObject(_data);
165  r->_success = true;
166  }
167  else
168  {
169  r->_reason = _reason;
170  r->_success = false;
171  }
172  }
173 }
174 
181 {
182  if (_getting)
183  { _requests << r;
184  return;
185  }
186 
187  VFS::LOG("Requesting "+_url.toString(QUrl::FullyEncoded),9,className());
188 
189  //VFS_request *s = r->getCallback(this); //don't do this
190  r->_isCallback = true; //defer the response as if there were a callback.
191 
192  QNetworkRequest request(_url);
193  request.setAttribute(QNetworkRequest::FollowRedirectsAttribute,true);
194  //request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
195 
196  //QNetworkReply *p = _manager.post(request,params.query().toUtf8());
197  //QNetworkReply *p = _manager.get( request );
198  _request = _manager.get( request );
199 
200  //connect( _request, SIGNAL(QNetworkReply::error(QNetworkReply::NetworkError code) ),
201  // this, SLOT(networkReplyError(QNetworkReply::NetworkError code) );
202 
203  _timer.start(_timeout);
204 
205  _requests << r;
206 
207  _getting = true;
208 }
209 
217 void VFS_curl::replyFinished(QNetworkReply *reply)
218 {
219  VFS::LOG("Request completed: "+_url.toString(QUrl::FullyEncoded),9,className());
220 
221  _getting = false;
222  _stale = false;
223  QTimer::singleShot( _expire, this, SLOT(expire()) );
224 
225  _timer.stop(); //will restart after expire
226 
227  QByteArray data;
228 
229  if (reply->error())
230  {
231  VFS::ERROR( _reason = reply->errorString(), 0, className() );
232  _valid = false;
233  _metadata = QJsonObject();
234  data = "";
235  }
236  else
237  { _reason = "";
238  _valid = true;
239  _metadata = getHeaders(reply);
240  data = reply->readAll();
241  }
242 
243  QJsonObject jsondata = _json ? QJsonDocument::fromJson(data).object() : QJsonObject { {"data",QString(data)} };
244  QJsonObject jsondiff;
245  _data = parse(jsondata,jsondiff);
246 
247  while (!_requests.isEmpty())
248  {
249  VFS_request *r = _requests.takeFirst();
250  r->_success = _valid;
251 
252  if (!_valid)
253  {
254  r->_reason = QString("Curl error: %1").arg(_reason);
255  }
256  else
257  {
258  switch (r->_requestType)
259  {
260  case VFS_request::read: r->_data.setObject( _data );
261  break;
262 
263  case VFS_request::metadata: r->_metadata["http"] = _metadata;
264  break;
265 
266  case VFS_request::subscribe: r->_metadata["http"] = _metadata;
267  r->_data.setObject( _data );
268  break;
269 
270  case VFS_request::diff: //the case where the current data has been expired and polled
271  if (!jsondiff.isEmpty())
272  {
273  r->_data.setObject(jsondiff);
274 
275  //VFS::WARN("emit diff here!");
276  //printf("%s\n",qUtf8Printable(r->toJson(0,true)));
277 
278  emit diff(this,r);
279  }
280 
281  delete r;
282  continue;
283  //break;
284 
285  default: break;
286  }
287  }
288 
289  issueResponse(r);
290  }
291 
292  reply->deleteLater();
293 }
294 
303 {
304  VFS::WARN("Request timeout: "+_url.toString(QUrl::FullyEncoded),0,className());
305 
306  if (_request)
307  _request->abort();
308 
309  _request = nullptr;
310 }
311 
317 {
318  _stale = true;
319 
320  if (_poll)
321  {
322  VFS_request *r = createRequest(VFS_request::diff,"",className(),QJsonDocument());
323  //r->_isCallback = true;
324  get(r);
325  }
326 
327 }
328 
335 QJsonObject VFS_curl::getHeaders(QNetworkReply *reply)
336 {
337  /*
338  static QMap<QNetworkRequest::KnownHeaders, QString> headers = {
339  { QNetworkRequest::ContentDispositionHeader, "ContentDispositionHeader" },
340  { QNetworkRequest::ContentTypeHeader, "ContentTypeHeader" },
341  { QNetworkRequest::ContentLengthHeader, "ContentLengthHeader" },
342  { QNetworkRequest::LocationHeader, "LocationHeader" },
343  { QNetworkRequest::LastModifiedHeader, "LastModifiedHeader" },
344  { QNetworkRequest::CookieHeader, "CookieHeader" },
345  { QNetworkRequest::SetCookieHeader, "SetCookieHeader" },
346  { QNetworkRequest::UserAgentHeader, "UserAgentHeader" },
347  { QNetworkRequest::ServerHeader, "ServerHeader" }
348  };
349 
350  QJsonObject m;
351 
352  QMap<QNetworkRequest::KnownHeaders,QString>::iterator h = headers.begin();
353  while (h != headers.end())
354  {
355  QVariant v = reply->header(h.key());
356  if(v.isValid())
357  m[h.value()] = v.to
358 
359  h++;
360  }*/
361 
362  QJsonObject m;
363 
364  QList<QByteArray> headers = reply->rawHeaderList();
365  foreach (QByteArray h,headers)
366  m[QString(h)] = QString(reply->rawHeader(h));
367 
368  return m;
369 }
370 
371 
378 void VFS_curl::sslErrors(QNetworkReply *reply,const QList<QSslError> &errors)
379 {
380  //Q_UNUSED(reply)
381 
382  for (int i=0;i<errors.size();i++)
383  VFS::ERROR( errors.at(i).errorString(), 0, className() );
384 
385  replyFinished(reply);
386 }
387 
400 QJsonObject VFS_curl::parse(QJsonObject data, QJsonObject &diffout)
401 {
402  //VFS_request *r = createRequest(VFS_request::diff,"",className(),QJsonDocument(data));
403  //emit diff(this,r);
404 
405  diffout = data;
406 
407  return data;
408 }
bool _stale
If the retrieved content is stale. Initialized as true.
Definition: VFS_curl.h:46
virtual void read(VFS_request *r)
Request the _url.
Definition: VFS_curl.cpp:153
bool _json
Interpret fetched data as json.
Definition: VFS_curl.h:31
virtual void sslErrors(QNetworkReply *, const QList< QSslError > &errors)
One or more ssl errors have occurred.
Definition: VFS_curl.cpp:378
QNetworkReply * _request
The outstanding request, if any.
Definition: VFS_curl.h:53
QJsonObject _data
The data retrieved from a http get request after it has been sent to parse().
Definition: VFS_curl.h:38
virtual bool isContainer()
A VFS_curl node cannot contain children.
Definition: VFS_curl.cpp:100
bool _poll
If data is considered expired, re-request it.
Definition: VFS_curl.h:32
virtual QByteArray icon()
The "stream" icon found in the VFS_icons library.
Definition: VFS_curl.cpp:80
virtual QString reportDetails()
Report the url.
Definition: VFS_curl.cpp:90
virtual void expire()
Set the _stale value to true, and if _poll, request an update.
Definition: VFS_curl.cpp:316
int _timeout
The time to wait for a response before timing out. Defaults to 60000 ms = 1 minute.
Definition: VFS_curl.h:52
QJsonObject getHeaders(QNetworkReply *reply)
Create a QJsonObject based on a QNetworkReply.
Definition: VFS_curl.cpp:335
virtual void get(VFS_request *r)
VFS_curl::get.
Definition: VFS_curl.cpp:180
Q_INVOKABLE VFS_curl(QString url, bool json=false, bool poll=false, int expire=10000, int timeout=60000)
Definition: VFS_curl.cpp:33
virtual void networkTimeout()
A request was initiated, but timed out.
Definition: VFS_curl.cpp:302
void replyFinished(QNetworkReply *reply)
A network request has completed.
Definition: VFS_curl.cpp:217
QTimer _timer
For detecting timeouts on network requests. The QNetworkAccessManager doesn't do this on its own.
Definition: VFS_curl.h:51
QString _reason
If an http request was invalid, this will contain the error string.
Definition: VFS_curl.h:49
QList< VFS_request * > _requests
A list of outstanding VFS_request objects to satisfy once an http request is complete.
Definition: VFS_curl.h:35
virtual QJsonObject parse(QJsonObject data, QJsonObject &diffout)
Parse the incoming data into useful format.
Definition: VFS_curl.cpp:400
bool _valid
If the http request was valid.
Definition: VFS_curl.h:47
QNetworkAccessManager _manager
The http request manager.
Definition: VFS_curl.h:34
int _expire
Time in milliseconds to consider the retrieved data stale.
Definition: VFS_curl.h:33
QJsonObject _metadata
Certain headers returned from a successful http get request.
Definition: VFS_curl.h:37
bool _getting
If an outstanding request exists.
Definition: VFS_curl.h:48
virtual ~VFS_curl()
Definition: VFS_curl.cpp:69
QUrl _url
The url to request.
Definition: VFS_curl.h:30
virtual void metadata(VFS_request *r)
Fetch the metadata for this node.
Definition: VFS_curl.cpp:114
static char * get(QString which="")
Fetch an icon from the library.
Definition: VFS_icons.cpp:34
VFS_node is the base class from which all other VFS_node classes derive.
Definition: VFS_node.h:143
virtual VFS_request * createRequest(VFS_request::requestType type, QString path, QString user="unknown", QJsonDocument data=QJsonDocument(), QJsonObject metadata=QJsonObject(), bool dontDelete=false)
Create a new VFS_request with this object as _origin.
Definition: VFS_node.cpp:1913
virtual void issueResponse(VFS_request *t)
Once a request has been completed, issue a response.
Definition: VFS_node.cpp:1981
virtual void metadata(VFS_request *r)
Fetch the metadata of this node.
Definition: VFS_node.cpp:797
void diff(VFS_node *origin, VFS_request *t)
Emit a diff, which will trigger notifySubscribers() for a mounted node.
QString className()
Return the class name of a node.
Definition: VFS_node.cpp:2039
void finished(bool andDelete=false)
Emitted if a thread fails to create its node, or a node is rm()'d, or any other reason a node has com...
The base class for all requests between nodes.
Definition: VFS_node.h:54
@ diff
send a diff (7)
Definition: VFS_node.h:71
@ read
read full contents (4)
Definition: VFS_node.h:68
@ metadata
read metadata (6)
Definition: VFS_node.h:70
@ subscribe
subscribe to a path (9)
Definition: VFS_node.h:73
requestType _requestType
the action this request is performing or requesting
Definition: VFS_node.h:87
QString _reason
if something (probably bad) happened, this is the reason
Definition: VFS_node.h:108
bool _isCallback
whether or not to issue a response (IE, another request is chained to this request,...
Definition: VFS_node.h:98
bool _success
if the request was successfully completed
Definition: VFS_node.h:107
QJsonDocument _data
the request payload
Definition: VFS_node.h:102
QJsonObject _metadata
the request payload
Definition: VFS_node.h:101
static void LOG(QString message, int level=0, QString user="server")
Send a message to the VFS::_messages VFS_stream.
Definition: VFS.cpp:209
static void ERROR(QString message, int level=0, QString user="server")
Send a message to the VFS::_errors VFS_stream.
Definition: VFS.cpp:307
static void WARN(QString message, int level=0, QString user="server")
Send a message to the VFS::_warnings VFS_stream.
Definition: VFS.cpp:258
url(value, options)