Overte C++ Documentation
XMLHttpRequestClass.h
1 //
2 // XMLHttpRequestClass.h
3 // libraries/script-engine/src/
4 //
5 // Created by Ryan Huffman on 5/2/14.
6 // Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
7 // Copyright 2023 Overte e.V.
8 //
9 // Distributed under the Apache License, Version 2.0.
10 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11 // SPDX-License-Identifier: Apache-2.0
12 //
13 
16 
17 #ifndef hifi_XMLHttpRequestClass_h
18 #define hifi_XMLHttpRequestClass_h
19 
20 #include <QBuffer>
21 #include <QNetworkReply>
22 #include <QNetworkRequest>
23 #include <QObject>
24 #include <QTimer>
25 
26 #include "ScriptEngine.h"
27 #include "ScriptValue.h"
28 
29 class ScriptContext;
30 
31 /*
32 XMlHttpRequest object
33 XMlHttpRequest.objectName string
34 XMlHttpRequest.response undefined
35 XMlHttpRequest.responseText string
36 XMlHttpRequest.responseType string
37 XMlHttpRequest.status number
38 XMlHttpRequest.statusText string
39 XMlHttpRequest.readyState number
40 XMlHttpRequest.errorCode number
41 XMlHttpRequest.timeout number
42 XMlHttpRequest.UNSENT number
43 XMlHttpRequest.OPENED number
44 XMlHttpRequest.HEADERS_RECEIVED number
45 XMlHttpRequest.LOADING number
46 XMlHttpRequest.DONE number
47 XMlHttpRequest.ontimeout object
48 XMlHttpRequest.onreadystatechange object
49 XMlHttpRequest.destroyed(QObject*) function
50 XMlHttpRequest.destroyed() function
51 XMlHttpRequest.objectNameChanged(QString) function
52 XMlHttpRequest.deleteLater() function
53 XMlHttpRequest.requestComplete() function
54 XMlHttpRequest.abort() function
55 XMlHttpRequest.setRequestHeader(QString,QString) function
56 XMlHttpRequest.open(QString,QString,bool,QString,QString) function
57 XMlHttpRequest.open(QString,QString,bool,QString) function
58 XMlHttpRequest.open(QString,QString,bool) function
59 XMlHttpRequest.open(QString,QString) function
60 XMlHttpRequest.send() function
61 XMlHttpRequest.send(ScriptValue) function
62 XMlHttpRequest.getAllResponseHeaders() function
63 XMlHttpRequest.getResponseHeader(QString) function
64 */
65 
66 /*@jsdoc
67  * Provides a means to interact with web servers. It is a near-complete implementation of the XMLHttpRequest API described in
68  * the Mozilla docs:
69  * <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest</a>.
70  *
71  * <p>Create using <code>new XMLHttpRequest(...)</code>.</p>
72  *
73  * @class XMLHttpRequest
74  *
75  * @hifi-interface
76  * @hifi-client-entity
77  * @hifi-avatar
78  * @hifi-server-entity
79  * @hifi-assignment-client
80  *
81  * @property {*} response - The response data.
82  * <em>Read-only.</em>
83  * @property {string} responseText - The response data as text.
84  * <em>Read-only.</em>
85  * @property {string} responseType - The response type required or received (e.g., <code>"text"</code>, <code>"json"</code>,
86  * <code>"arraybuffer"</code>, ...).
87  * @property {number} status - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status">HTTP response status
88  * code</a> (<code>100</code> &ndash; <code>599</code>).
89  * <em>Read-only.</em>
90  * @property {string} statusText - The HTTP response status text.
91  * <em>Read-only.</em>
92  * @property {XMLHttpRequest.ReadyState} readyState - The status of the request.
93  * <em>Read-only.</em>
94  * @property {XMLHttpRequest.NetworkError} errorCode - The network result of the request: including, <code>0</code> (NoError)
95  * if there was no error, <code>4</code> (TimeoutError) if the request timed out.
96  * <em>Read-only.</em>
97  * @property {number} timeout - The time a request can take before timing out, in ms.
98  *
99  * @property {XMLHttpRequest.ReadyState} UNSENT - Request has been created; {@link XMLHttpRequest.open} not called yet.
100  * <em>Read-only.</em>
101  * @property {XMLHttpRequest.ReadyState} OPENED - {@link XMLHttpRequest.open} has been called.
102  * <em>Read-only.</em>
103  * @property {XMLHttpRequest.ReadyState} HEADERS_RECEIVED - {@link XMLHttpRequest.send} has been called; headers and status
104  * are available.
105  * <em>Read-only.</em>
106  * @property {XMLHttpRequest.ReadyState} LOADING - Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has partial
107  * data.
108  * <em>Read-only.</em>
109  * @property {XMLHttpRequest.ReadyState} DONE - Operation complete.
110  * <em>Read-only.</em>
111  *
112  * @property {XMLHttpRequest~onTimeoutCallback} ontimeout - Function called when the request times out.
113  * <p>Note: This is called in addition to any function set for <code>onreadystatechange</code>.</p>
114  * @property {XMLHttpRequest~onReadyStateChangeCallback} onreadystatechange - Function called when the request's ready state
115  * changes.
116  *
117  * @example <caption>Get a web page's HTML.</caption>
118  * var URL = "https://www.highfidelity.com/";
119  *
120  * var req = new XMLHttpRequest();
121  * req.onreadystatechange = function () {
122  * if (req.readyState === req.DONE) {
123  * if (req.status === 200) {
124  * print("Success");
125  * print("Content type:", req.getResponseHeader("content-type"));
126  * print("Content:", req.responseText.slice(0, 100), "...");
127  *
128  * } else {
129  * print("Error", req.status, req.statusText);
130  * }
131  *
132  * req = null;
133  * }
134  * };
135  *
136  * req.open("GET", URL);
137  * req.send();
138  *
139  * @example <caption>Get a web page's HTML &mdash; alternative method.</caption>
140  * var URL = "https://www.highfidelity.com/";
141  *
142  * var req = new XMLHttpRequest();
143  * req.requestComplete.connect(function () {
144  * if (req.status === 200) {
145  * print("Success");
146  * print("Content type:", req.getResponseHeader("content-type"));
147  * print("Content:", req.responseText.slice(0, 100), "...");
148  *
149  * } else {
150  * print("Error", req.status, req.statusText);
151  * }
152  *
153  * req = null;
154  * });
155  *
156  * req.open("GET", URL);
157  * req.send();
158  */
160 class XMLHttpRequestClass : public QObject {
161  Q_OBJECT
162  Q_PROPERTY(ScriptValue response READ getResponse)
163  Q_PROPERTY(ScriptValue responseText READ getResponseText)
164  Q_PROPERTY(QString responseType READ getResponseType WRITE setResponseType)
165  Q_PROPERTY(ScriptValue status READ getStatus)
166  Q_PROPERTY(QString statusText READ getStatusText)
167  Q_PROPERTY(ScriptValue readyState READ getReadyState)
168  Q_PROPERTY(ScriptValue errorCode READ getError)
169  Q_PROPERTY(int timeout READ getTimeout WRITE setTimeout)
170 
171  Q_PROPERTY(int UNSENT READ getUnsent)
172  Q_PROPERTY(int OPENED READ getOpened)
173  Q_PROPERTY(int HEADERS_RECEIVED READ getHeadersReceived)
174  Q_PROPERTY(int LOADING READ getLoading)
175  Q_PROPERTY(int DONE READ getDone)
176 
177  // Callbacks
178  Q_PROPERTY(ScriptValue ontimeout READ getOnTimeout WRITE setOnTimeout)
179  Q_PROPERTY(ScriptValue onreadystatechange READ getOnReadyStateChange WRITE setOnReadyStateChange)
180 public:
183 
184  static const int MAXIMUM_REDIRECTS = 5;
185 
186  /*@jsdoc
187  * <p>The state of an XMLHttpRequest.</p>
188  * <table>
189  * <thead>
190  * <tr><th>Value</th><th>Name</th><th>Description</th></tr>
191  * </thead>
192  * <tbody>
193  * <tr><td><code>0</code></td><td>UNSENT</td><td>Request has been created; {@link XMLHttpRequest.open} not called
194  * yet.</td></tr>
195  * <tr><td><code>1</code></td><td>OPENED</td><td>{@link XMLHttpRequest.open} has been called.</td></tr>
196  * <tr><td><code>2</code></td><td>HEADERS_RECEIVED</td><td>{@link XMLHttpRequest.send} has been called; headers and
197  * status are available.</td></tr>
198  * <tr><td><code>3</code></td><td>LOADING</td><td>Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has
199  * partial data.</td></tr>
200  * <tr><td><code>4</code></td><td>DONE</td><td>Operation complete.</td></tr>
201  * </tbody>
202  * </table>
203  * @typedef {number} XMLHttpRequest.ReadyState
204  */
205  enum ReadyState {
206  UNSENT = 0,
207  OPENED,
208  HEADERS_RECEIVED,
209  LOADING,
210  DONE
211  };
212 
213  int getUnsent() const { return UNSENT; };
214  int getOpened() const { return OPENED; };
215  int getHeadersReceived() const { return HEADERS_RECEIVED; };
216  int getLoading() const { return LOADING; };
217  int getDone() const { return DONE; };
218 
219  static ScriptValue constructor(ScriptContext* context, ScriptEngine* engine);
220 
221  int getTimeout() const { return _timeout; }
222  void setTimeout(int timeout) { _timeout = timeout; }
223  ScriptValue getResponse() const { return _responseData; }
224  ScriptValue getResponseText() const { return _engine->newValue(QString(_rawResponseData.data())); }
225  QString getResponseType() const { return _responseType; }
226  void setResponseType(const QString& responseType) { _responseType = responseType; }
227  ScriptValue getReadyState() const { return _engine->newValue(_readyState); }
228  ScriptValue getError() const { return _engine->newValue(_errorCode); }
229  ScriptValue getStatus() const;
230  QString getStatusText() const;
231 
232  ScriptValue getOnTimeout() const { return _onTimeout; }
233  void setOnTimeout(const ScriptValue& function) { _onTimeout = function; }
234  ScriptValue getOnReadyStateChange() const { return _onReadyStateChange; }
235  void setOnReadyStateChange(const ScriptValue& function) { _onReadyStateChange = function; }
236 
237 public slots:
238 
239  /*@jsdoc
240  * Aborts the request.
241  * @function XMLHttpRequest.abort
242  */
243  void abort();
244 
245  /*@jsdoc
246  * Sets the value of an HTTP request header. Must be called after {@link XMLHttpRequest.open} but before
247  * {@link XMLHttpRequest.send};
248  * @function XMLHttpRequest.setRequestHeader
249  * @param {string} name - The name of the header to set.
250  * @param {string} value - The value of the header.
251  */
252  void setRequestHeader(const QString& name, const QString& value);
253 
254  /*@jsdoc
255  * Initializes a request.
256  * @function XMLHttpRequest.open
257  * @param {string} method - The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP request method</a>
258  * to use, e.g., <code>"GET"</code>, <code>"POST"</code>, <code>"PUT"</code>, or <code>"DELETE"</code>.
259  * @param {string} url - The URL to send the request to.
260  * @param {boolean} [async=true] - <code>true</code> if the method returns without waiting for the response,
261  * <code>false</code> if the method returns only once the request is complete.
262  * @param {string} [username=""] - User name for authentication.
263  * @param {string} [password=""] - Password for authentication.
264  */
265  void open(const QString& method, const QString& url, bool async = true, const QString& username = "",
266  const QString& password = "");
267 
268  /*@jsdoc
269  * Sends the request to the server.
270  * @function XMLHttpRequest.send
271  * @param {*} [data] - The data to send.
272  */
273  void send();
274  void send(const ScriptValue& data);
275 
276  /*@jsdoc
277  * Gets the response headers.
278  * @function XMLHttpRequest.getAllResponseHeaders
279  * @returns {string} The response headers, separated by <code>"\n"</code> characters.
280  */
281  ScriptValue getAllResponseHeaders() const;
282 
283  /*@jsdoc
284  * Gets a response header.
285  * @function XMLHttpRequest.getResponseHeader
286  * @param {string} name -
287  * @returns {string} The response header.
288  */
289  ScriptValue getResponseHeader(const QString& name) const;
290 
291 signals:
292 
293  /*@jsdoc
294  * Triggered when the request is complete &mdash; with or without error (incl. timeout).
295  * @function XMLHttpRequest.requestComplete
296  * @returns {Signal}
297  */
298  void requestComplete();
299 
300 private:
301  void setReadyState(ReadyState readyState);
302  void doSend();
303  void connectToReply(QNetworkReply* reply);
304  void disconnectFromReply(QNetworkReply* reply);
305  void abortRequest();
306 
307  ScriptEngine* _engine { nullptr };
308  bool _async { true };
309  QUrl _url;
310  QString _method;
311  QString _responseType;
312  QNetworkRequest _request;
313  QNetworkReply* _reply { nullptr };
314  QByteArray _sendData;
315  QByteArray _rawResponseData;
316  ScriptValue _responseData;
317  ScriptValue _onTimeout;
318  ScriptValue _onReadyStateChange;
319  ReadyState _readyState { XMLHttpRequestClass::UNSENT };
320 
321  /*@jsdoc
322  * <p>The type of network error.</p>
323  * <table>
324  * <thead>
325  * <tr><th>Value</th><th>Name</th><th>Description</th></tr>
326  * </thead>
327  * <tbody>
328  * <tr><td><code>0</code></td><td>NoError</td><td>No error.</td></tr>
329  * <tr><td><code>1</code></td><td>ConnectionRefusedError</td><td>The server refused the connection.</td></tr>
330  * <tr><td><code>2</code></td><td>RemoteHostClosedError</td><td>The server closed the connection.</td></tr>
331  * <tr><td><code>3</code></td><td>HostNotFoundError</td><td>Host name not found.</td></tr>
332  * <tr><td><code>4</code></td><td>TimeoutError</td><td>Connection timed out</td></tr>
333  * <tr><td><code>5</code></td><td>OperationCanceledError</td><td>Operation canceled by
334  * {@link XMLHttpRequest.abort}.</td></tr>
335  * <tr><td><code>6</code></td><td>SslHandshakeFailedError</td><td>SSL/TLS handshake failed.</td></tr>
336  * <tr><td><code>7</code></td><td>TemporaryNetworkFailureError</td><td>Temporarily disconnected from the
337  * network.</td></tr>
338  * <tr><td><code>8</code></td><td>NetworkSessionFailedError</td><td>Disconnection from the network.</td></tr>
339  * <tr><td><code>9</code></td><td>BackgroundRequestNotAllowedError</td><td>Background request not allowed.</td></tr>
340  * <tr><td><code>10</code></td><td>TooManyRedirectsError</td><td>Too many redirects.</td></tr>
341  * <tr><td><code>11</code></td><td>InsecureRedirectError</td><td>Redirect from secure to insecure protocol.</td></tr>
342  * <tr><td><code>101</code></td><td>ProxyConnectionRefusedError</td><td>Connection to proxy server refused.</td></tr>
343  * <tr><td><code>102</code></td><td>ProxyConnectionClosedError</td><td>Proxy server closed the connection.</td></tr>
344  * <tr><td><code>103</code></td><td>ProxyNotFoundError</td><td>Proxy host name not found.</td></tr>
345  * <tr><td><code>104</code></td><td>ProxyTimeoutError</td><td>Proxy connection timed out.</td></tr>
346  * <tr><td><code>105</code></td><td>ProxyAuthenticationRequiredError</td><td>Proxy requires authentication.</td></tr>
347  * <tr><td><code>201</code></td><td>ContentAccessDenied</td><td>Access denied.</td></tr>
348  * <tr><td><code>202</code></td><td>ContentOperationNotPermittedError</td><td>Operation not permitted.</td></tr>
349  * <tr><td><code>203</code></td><td>ContentNotFoundError</td><td>Content not found.</td></tr>
350  * <tr><td><code>204</code></td><td>AuthenticationRequiredError</td><td>Authentication required.</td></tr>
351  * <tr><td><code>205</code></td><td>ContentReSendError</td><td>Resend failed.</td></tr>
352  * <tr><td><code>206</code></td><td>ContentConflictError</td><td>Resource state conflict.</td></tr>
353  * <tr><td><code>207</code></td><td>ContentGoneError</td><td>Resource no longer available.</td></tr>
354  * <tr><td><code>401</code></td><td>InternalServerError</td><td>Internal server error.</td></tr>
355  * <tr><td><code>402</code></td><td>OperationNotImplementedError</td><td>Operation not supported.</td></tr>
356  * <tr><td><code>403</code></td><td>ServiceUnavailableError</td><td>Request not able to be handled at this
357  * time.</td></tr>
358  * <tr><td><code>301</code></td><td>ProtocolUnknownError</td><td>Protocol unknown.</td></tr>
359  * <tr><td><code>302</code></td><td>ProtocolInvalidOperationError</td><td>Operation invalid fro protocol.</td></tr>
360  * <tr><td><code>99</code></td><td>UnknownNetworkError</td><td>Unknown network-related error.</td></tr>
361  * <tr><td><code>199</code></td><td>UnknownProxyError</td><td>Unknown proxy-related error.</td></tr>
362  * <tr><td><code>299</code></td><td>UnknownContentError</td><td>Unknown content-related error.</td></tr>
363  * <tr><td><code>399</code></td><td>ProtocolFailure</td><td>Protocol error.</td></tr>
364  * <tr><td><code>499</code></td><td>UnknownServerError</td><td>Unknown server response error.</td></tr>
365 
366  * </tbody>
367  * </table>
368  * @typedef {number} XMLHttpRequest.NetworkError
369  */
370  QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError };
371 
372  int _timeout { 0 };
373  QTimer _timer;
374  int _numRedirects { 0 };
375 
376 private slots:
377  void requestFinished();
378  void requestError(QNetworkReply::NetworkError code);
379  void requestMetaDataChanged();
380  void requestDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
381  void requestTimeout();
382 };
383 
384 #endif // hifi_XMLHttpRequestClass_h
385 
[ScriptInterface] Provides an engine-independent interface for QScriptContext
Definition: ScriptContext.h:55
Provides an engine-independent interface for a scripting engine.
Definition: ScriptEngine.h:93
[ScriptInterface] Provides an engine-independent interface for QScriptValue
Definition: ScriptValue.h:40
Provides the XMLHttpRequest scripting interface.
Definition: XMLHttpRequestClass.h:160