/**jsdoc
- * Provides a bi-direcctional, event-driven communication session between the script and another WebSocket connection. It is a
+ * Provides a bi-directional, event-driven communication session between the script and another WebSocket connection. It is a
* near-complete implementation of the WebSocket API described in the Mozilla docs:
- * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket.
+ * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket.
*
* Constructed by new WebSocket(...)
in Interface, client entity, avatar, and server entity scripts, or the
* {@link WebSocketServer} class in server entity and assignment client scripts.
diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp
index 571c0e2a0e..57514a2231 100644
--- a/libraries/script-engine/src/XMLHttpRequestClass.cpp
+++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp
@@ -110,6 +110,10 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const {
return QScriptValue::NullValue;
}
+/**jsdoc
+ * Called when the request's ready state changes.
+ * @callback XMLHttpRequest~onReadyStateChangeCallback
+ */
void XMLHttpRequestClass::setReadyState(ReadyState readyState) {
if (readyState != _readyState) {
_readyState = readyState;
@@ -183,6 +187,10 @@ void XMLHttpRequestClass::doSend() {
}
}
+/**jsdoc
+ * Called when the request times out.
+ * @callback XMLHttpRequest~onTimeoutCallback
+ */
void XMLHttpRequestClass::requestTimeout() {
if (_onTimeout.isFunction()) {
_onTimeout.call(QScriptValue::NullValue);
diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h
index d7f3c2e059..cb86cafb67 100644
--- a/libraries/script-engine/src/XMLHttpRequestClass.h
+++ b/libraries/script-engine/src/XMLHttpRequestClass.h
@@ -21,6 +21,132 @@
#include
#include
+/*
+XMlHttpRequest object
+XMlHttpRequest.objectName string
+XMlHttpRequest.response undefined
+XMlHttpRequest.responseText string
+XMlHttpRequest.responseType string
+XMlHttpRequest.status number
+XMlHttpRequest.statusText string
+XMlHttpRequest.readyState number
+XMlHttpRequest.errorCode number
+XMlHttpRequest.timeout number
+XMlHttpRequest.UNSENT number
+XMlHttpRequest.OPENED number
+XMlHttpRequest.HEADERS_RECEIVED number
+XMlHttpRequest.LOADING number
+XMlHttpRequest.DONE number
+XMlHttpRequest.ontimeout object
+XMlHttpRequest.onreadystatechange object
+XMlHttpRequest.destroyed(QObject*) function
+XMlHttpRequest.destroyed() function
+XMlHttpRequest.objectNameChanged(QString) function
+XMlHttpRequest.deleteLater() function
+XMlHttpRequest.requestComplete() function
+XMlHttpRequest.abort() function
+XMlHttpRequest.setRequestHeader(QString,QString) function
+XMlHttpRequest.open(QString,QString,bool,QString,QString) function
+XMlHttpRequest.open(QString,QString,bool,QString) function
+XMlHttpRequest.open(QString,QString,bool) function
+XMlHttpRequest.open(QString,QString) function
+XMlHttpRequest.send() function
+XMlHttpRequest.send(QScriptValue) function
+XMlHttpRequest.getAllResponseHeaders() function
+XMlHttpRequest.getResponseHeader(QString) function
+*/
+
+/**jsdoc
+ * Provides a means to interact with web servers. It is a near-complete implementation of the XMLHttpRequest API described in
+ * the Mozilla docs:
+ * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest.
+ *
+ * @class XMLHttpRequest
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-avatar
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
+ * @property {*} response - The response data.
+ * Read-only.
+ * @property {string} responseText - The response data as text.
+ * Read-only.
+ * @property {string} responseType - The response type required or received (e.g., "text"
, "json"
,
+ * "arraybuffer"
, ...).
+ * @property {number} status - The HTTP response status
+ * code (100
– 599
).
+ * Read-only.
+ * @property {string} statusText - The HTTP response status text.
+ * Read-only.
+ * @property {XMLHttpRequest.ReadyState} readyState - The status of the request.
+ * Read-only.
+ * @property {XMLHttpRequest.NetworkError} errorCode - The network result of the request: including, 0
(NoError)
+ * if there was no error, 4
(TimeoutError) if the request timed out.
+ * Read-only.
+ * @property {number} timeout - The time a request can take before timing out, in ms.
+ *
+ * @property {XMLHttpRequest.ReadyState} UNSENT - Request has been created; {@link XMLHttpRequest.open} not called yet.
+ * Read-only.
+ * @property {XMLHttpRequest.ReadyState} OPENED - {@link XMLHttpRequest.open} has been called.
+ * Read-only.
+ * @property {XMLHttpRequest.ReadyState} HEADERS_RECEIVED - {@link XMLHttpRequest.send} has been called; headers and status
+ * are available.
+ * Read-only.
+ * @property {XMLHttpRequest.ReadyState} LOADING - Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has partial
+ * data.
+ * Read-only.
+ * @property {XMLHttpRequest.ReadyState} DONE - Operation complete.
+ * Read-only.
+ *
+ * @property {XMLHttpRequest~onTimeoutCallback} ontimeout - Function called when the request times out.
+ * Note: This is called in addition to any function set for onreadystatechange
.
+ * @property {XMLHttpRequest~onReadyStateChangeCallback} onreadystatechange - Function called when the request's ready state
+ * changes.
+ *
+ * @example Get a web page's HTML.
+ * var URL = "https://www.highfidelity.com/";
+ *
+ * var req = new XMLHttpRequest();
+ * req.onreadystatechange = function () {
+ * if (req.readyState === req.DONE) {
+ * if (req.status === 200) {
+ * print("Success");
+ * print("Content type:", req.getResponseHeader("content-type"));
+ * print("Content:", req.responseText.slice(0, 100), "...");
+ *
+ * } else {
+ * print("Error", req.status, req.statusText);
+ * }
+ *
+ * req = null;
+ * }
+ * };
+ *
+ * req.open("GET", URL);
+ * req.send();
+ *
+ * @example Get a web page's HTML — alternative method.
+ * var URL = "https://www.highfidelity.com/";
+ *
+ * var req = new XMLHttpRequest();
+ * req.requestComplete.connect(function () {
+ * if (req.status === 200) {
+ * print("Success");
+ * print("Content type:", req.getResponseHeader("content-type"));
+ * print("Content:", req.responseText.slice(0, 100), "...");
+ *
+ * } else {
+ * print("Error", req.status, req.statusText);
+ * }
+ *
+ * req = null;
+ * });
+ *
+ * req.open("GET", URL);
+ * req.send();
+ */
class XMLHttpRequestClass : public QObject {
Q_OBJECT
Q_PROPERTY(QScriptValue response READ getResponse)
@@ -46,6 +172,26 @@ public:
~XMLHttpRequestClass();
static const int MAXIMUM_REDIRECTS = 5;
+
+ /**jsdoc
+ * The state of an XMLHttpRequest.
+ *
+ *
+ * Value | Name | Description |
+ *
+ *
+ * 0 | UNSENT | Request has been created; {@link XMLHttpRequest.open} not called
+ * yet. |
+ * 1 | OPENED | {@link XMLHttpRequest.open} has been called. |
+ * 2 | HEADERS_RECEIVED | {@link XMLHttpRequest.send} has been called; headers and
+ * status are available. |
+ * 3 | LOADING | Downloading; {@link XMLHttpRequest|XMLHttpRequest.responseText} has
+ * partial data. |
+ * 4 | DONE | Operation complete. |
+ *
+ *
+ * @typedef {number} XMLHttpRequest.ReadyState
+ */
enum ReadyState {
UNSENT = 0,
OPENED,
@@ -79,16 +225,66 @@ public:
void setOnReadyStateChange(QScriptValue function) { _onReadyStateChange = function; }
public slots:
+
+ /**jsdoc
+ * Aborts the request.
+ * @function XMLHttpRequest.abort
+ */
void abort();
+
+ /**jsdoc
+ * Sets the value of an HTTP request header. Must be called after {@link XMLHttpRequest.open} but before
+ * {@link XMLHttpRequest.send};
+ * @function XMLHttpRequest.setRequestHeader
+ * @param {string} name - The name of the header to set.
+ * @param {string} value - The value of the header.
+ */
void setRequestHeader(const QString& name, const QString& value);
+
+ /**jsdoc
+ * Initializes a request.
+ * @function XMLHttpRequest.open
+ * @param {string} method - The HTTP request method
+ * to use, e.g., "GET"
, "POST"
, "PUT"
, or "DELETE"
.
+ * @param {string} url - The URL to send the request to.
+ * @param {boolean} [async=true] - true
if the method returns without waiting for the response,
+ * false
if the method returns only once the request is complete.
+ * @param {string} [username=""] - User name for authentication.
+ * @param {string} [password=""] - Password for authentication.
+ */
void open(const QString& method, const QString& url, bool async = true, const QString& username = "",
const QString& password = "");
+
+ /**jsdoc
+ * Sends the request to the server.
+ * @function XMLHttpRequest.send
+ * @param {*} [data] - The data to send.
+ */
void send();
void send(const QScriptValue& data);
+
+ /**jsdoc
+ * Gets the response headers.
+ * @function XMLHttpRequest.getAllResponseHeaders
+ * @returns {string} The response headers, separated by "\n"
characters.
+ */
QScriptValue getAllResponseHeaders() const;
+
+ /**jsdoc
+ * Gets a response header.
+ * @function XMLHttpRequest.getResponseHeader
+ * @param {string} name -
+ * @returns {string} The response header.
+ */
QScriptValue getResponseHeader(const QString& name) const;
signals:
+
+ /**jsdoc
+ * Triggered when the request is complete — with or without error (incl. timeout).
+ * @function XMLHttpRequest.requestComplete
+ * @returns {Signal}
+ */
void requestComplete();
private:
@@ -111,7 +307,58 @@ private:
QScriptValue _onTimeout { QScriptValue::NullValue };
QScriptValue _onReadyStateChange { QScriptValue::NullValue };
ReadyState _readyState { XMLHttpRequestClass::UNSENT };
+
+ /**jsdoc
+ * The type of network error.
+ *
+ *
+ * Value | Name | Description |
+ *
+ *
+ * 0 | NoError | No error. |
+ * 1 | ConnectionRefusedError | The server refused the connection. |
+ * 2 | RemoteHostClosedError | The server closed the connection. |
+ * 3 | HostNotFoundError | Host name not found. |
+ * 4 | TimeoutError | Connection timed out |
+ * 5 | OperationCanceledError | Operation canceled by
+ * {@link XMLHttpRequest.abort}. |
+ * 6 | SslHandshakeFailedError | SSL/TLS handshake failed. |
+ * 7 | TemporaryNetworkFailureError | Temporarily disconnected from the
+ * network. |
+ * 8 | NetworkSessionFailedError | Disconnection from the network. |
+ * 9 | BackgroundRequestNotAllowedError | Background request not allowed. |
+ * 10 | TooManyRedirectsError | Too many redirects. |
+ * 11 | InsecureRedirectError | Redirect from secure to insecure protocol. |
+ * 101 | ProxyConnectionRefusedError | Connection to proxy server refused. |
+ * 102 | ProxyConnectionClosedError | Proxy server closed the connection. |
+ * 103 | ProxyNotFoundError | Proxy host name not found. |
+ * 104 | ProxyTimeoutError | Proxy connection timed out. |
+ * 105 | ProxyAuthenticationRequiredError | Proxy requires authentication. |
+ * 201 | ContentAccessDenied | Access denied. |
+ * 202 | ContentOperationNotPermittedError | Operation not permitted. |
+ * 203 | ContentNotFoundError | Content not found. |
+ * 204 | AuthenticationRequiredError | Authentication required. |
+ * 205 | ContentReSendError | Resend failed. |
+ * 206 | ContentConflictError | Resource state conflict. |
+ * 207 | ContentGoneError | Resource no longer available. |
+ * 401 | InternalServerError | Internal server error. |
+ * 402 | OperationNotImplementedError | Operation not supported. |
+ * 403 | ServiceUnavailableError | Request not able to be handled at this
+ * time. |
+ * 301 | ProtocolUnknownError | Protocol unknown. |
+ * 302 | ProtocolInvalidOperationError | Operation invalid fro protocol. |
+ * 99 | UnknownNetworkError | Unknown network-related error. |
+ * 199 | UnknownProxyError | Unknown proxy-related error. |
+ * 299 | UnknownContentError | Unknown content-related error. |
+ * 399 | ProtocolFailure | Protocol error. |
+ * 499 | UnknownServerError | Unknown server response error. |
+
+ *
+ *
+ * @typedef {number} XMLHttpRequest.NetworkError
+ */
QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError };
+
int _timeout { 0 };
QTimer _timer;
int _numRedirects { 0 };