pax_global_header00006660000000000000000000000064147252605120014516gustar00rootroot0000000000000052 comment=76de70f61b8644484c9e5c40ec613bc222c27759 node-XMLHttpRequest-master/000077500000000000000000000000001472526051200161475ustar00rootroot00000000000000node-XMLHttpRequest-master/.github/000077500000000000000000000000001472526051200175075ustar00rootroot00000000000000node-XMLHttpRequest-master/.github/workflows/000077500000000000000000000000001472526051200215445ustar00rootroot00000000000000node-XMLHttpRequest-master/.github/workflows/test.yml000066400000000000000000000006661472526051200232560ustar00rootroot00000000000000name: Test on: push: branches: [ master ] pull_request: branches: [ master ] jobs: integration-tests: runs-on: ubuntu-latest strategy: matrix: node-version: [12.x, 20.x, 22.x, 23.x] steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm testnode-XMLHttpRequest-master/LICENSE000066400000000000000000000020651472526051200171570ustar00rootroot00000000000000 Copyright (c) 2010 passive.ly LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. node-XMLHttpRequest-master/README.md000066400000000000000000000066451472526051200174410ustar00rootroot00000000000000# node-XMLHttpRequest # Fork of [node-XMLHttpRequest](https://github.com/driverdan/node-XMLHttpRequest) by [driverdan](http://driverdan.com). Forked and published to npm because a [pull request](https://github.com/rase-/node-XMLHttpRequest/commit/a6b6f296e0a8278165c2d0270d9840b54d5eeadd) is not being created and merged. Changes made by [rase-](https://github.com/rase-/node-XMLHttpRequest/tree/add/ssl-support) are needed for [engine.io-client](https://github.com/Automattic/engine.io-client). ## Usage ## Here's how to include the module in your project and use as the browser-based XHR object. var XMLHttpRequest = require("xmlhttprequest-ssl").XMLHttpRequest; var xhr = new XMLHttpRequest(); Note: use the lowercase string "xmlhttprequest-ssl" in your require(). On case-sensitive systems (eg Linux) using uppercase letters won't work. ### Non-standard features ### Non-standard options for this module are passed through the `XMLHttpRequest` constructor. The following options control `https:` SSL requests: `ca`, `cert`, `ciphers`, `key`, `passphrase`, `pfx`, and `rejectUnauthorized`. You can find their functionality in the [Node.js docs](https://nodejs.org/api/https.html#httpsrequestoptions-callback). Additionally, the `agent` option allows you to specify a [Node.js Agent](https://nodejs.org/api/https.html#class-httpsagent) instance, allowing connection reuse. To prevent a process from not exiting naturally because a request socket from this module is still open, you can set `autoUnref` to a truthy value. This module allows control over the maximum number of redirects that are followed. You can set the `maxRedirects` option to do this. The default number is 20. Using the `allowFileSystemResources` option allows you to control access to the local filesystem through the `file:` protocol. The `origin` option allows you to set a base URL for the request. The resulting request URL will be constructed as follows `new URL(url, origin)`. # Original README # ## Versions ## Version 2.0.0 introduces a potentially breaking change concerning local file system requests. If these requests fail this library now returns the `errno` (or -1) as the response status code instead of returning status code 0. Prior to 1.4.0 version numbers were arbitrary. From 1.4.0 on they conform to the standard major.minor.bugfix. 1.x shouldn't necessarily be considered stable just because it's above 0.x. Since the XMLHttpRequest API is stable this library's API is stable as well. Major version numbers indicate significant core code changes. Minor versions indicate minor core code changes or better conformity to the W3C spec. ## License ## MIT license. See LICENSE for full details. ## Supports ## * Async and synchronous requests * GET, POST, PUT, and DELETE requests * All spec methods (open, send, abort, getRequestHeader, getAllRequestHeaders, event methods) * Requests to all domains ## Known Issues / Missing Features ## For a list of open issues or to report your own visit the [github issues page](https://github.com/driverdan/node-XMLHttpRequest/issues). * Local file access may have unexpected results for non-UTF8 files * Synchronous requests don't set headers properly * Synchronous requests freeze node while waiting for response (But that's what you want, right? Stick with async!). * Some events are missing, such as abort * getRequestHeader is case-sensitive * Cookies aren't persisted between requests * Missing XML support * Missing basic auth node-XMLHttpRequest-master/autotest.watchr000066400000000000000000000003121472526051200212250ustar00rootroot00000000000000def run_all_tests puts `clear` puts `node tests/test-constants.js` puts `node tests/test-headers.js` puts `node tests/test-request.js` end watch('.*.js') { run_all_tests } run_all_tests node-XMLHttpRequest-master/example/000077500000000000000000000000001472526051200176025ustar00rootroot00000000000000node-XMLHttpRequest-master/example/demo.js000066400000000000000000000006331472526051200210660ustar00rootroot00000000000000var sys = require('util'); var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { console.log("State: " + this.readyState); if (this.readyState == 4) { console.log("Complete.\nBody length: " + this.responseText.length); console.log("Body:\n" + this.responseText); } }; xhr.open("GET", "http://driverdan.com"); xhr.send(); node-XMLHttpRequest-master/lib/000077500000000000000000000000001472526051200167155ustar00rootroot00000000000000node-XMLHttpRequest-master/lib/XMLHttpRequest.js000066400000000000000000001035061472526051200221310ustar00rootroot00000000000000/** * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object. * * This can be used with JS designed for browsers to improve reuse of code and * allow the use of existing libraries. * * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs. * * @author Dan DeFelippi * @contributor David Ellis * @license MIT */ var fs = require('fs'); var spawn = require('child_process').spawn; /** * Module exports. */ module.exports = XMLHttpRequest; // backwards-compat XMLHttpRequest.XMLHttpRequest = XMLHttpRequest; /** * `XMLHttpRequest` constructor. * * Supported options for the `opts` object are: * * - `agent`: An http.Agent instance; http.globalAgent may be used; if 'undefined', agent usage is disabled * * @param {Object} opts optional "options" object */ function XMLHttpRequest(opts) { "use strict"; // defines a list of default options to prevent parameters pollution var default_options = { pfx: undefined, key: undefined, passphrase: undefined, cert: undefined, ca: undefined, ciphers: undefined, rejectUnauthorized: true, autoUnref: false, agent: undefined, allowFileSystemResources: true, maxRedirects: 20, // Chrome standard origin: undefined }; opts = Object.assign(default_options, opts); var sslOptions = { pfx: opts.pfx, key: opts.key, passphrase: opts.passphrase, cert: opts.cert, ca: opts.ca, ciphers: opts.ciphers, rejectUnauthorized: opts.rejectUnauthorized !== false }; /** * Private variables */ var self = this; var http = require('http'); var https = require('https'); var maxRedirects = opts.maxRedirects; if (typeof maxRedirects !== 'number' || Number.isNaN(maxRedirects)) maxRedirects = 20; else maxRedirects = Math.max(maxRedirects, 0); var redirectCount = 0; // Holds http.js objects var request; var response; // Request settings var settings = {}; // Disable header blacklist. // Not part of XHR specs. var disableHeaderCheck = false; // Set some default headers var defaultHeaders = { "User-Agent": "node-XMLHttpRequest", "Accept": "*/*" }; var headers = Object.assign({}, defaultHeaders); // These headers are not user setable. // The following are allowed but banned in the spec: // * user-agent var forbiddenRequestHeaders = [ "accept-charset", "accept-encoding", "access-control-request-headers", "access-control-request-method", "connection", "content-length", "content-transfer-encoding", "cookie", "cookie2", "date", "expect", "host", "keep-alive", "origin", "referer", "te", "trailer", "transfer-encoding", "upgrade", "via" ]; // These request methods are not allowed var forbiddenRequestMethods = [ "TRACE", "TRACK", "CONNECT" ]; // Send flag var sendFlag = false; // Error flag, used when errors occur or abort is called var errorFlag = false; var abortedFlag = false; // Event listeners var listeners = {}; /** * Constants */ this.UNSENT = 0; this.OPENED = 1; this.HEADERS_RECEIVED = 2; this.LOADING = 3; this.DONE = 4; /** * Public vars */ // Current state this.readyState = this.UNSENT; // default ready state change handler in case one is not set or is set late this.onreadystatechange = null; // Result & response this.responseText = ""; this.responseXML = ""; this.responseURL = ""; this.response = Buffer.alloc(0); this.status = null; this.statusText = null; // xhr.responseType is supported: // When responseType is 'text' or '', self.responseText will be utf8 decoded text. // When responseType is 'json', self.responseText initially will be utf8 decoded text, // which is then JSON parsed into self.response. // When responseType is 'arraybuffer', self.response is an ArrayBuffer. // When responseType is 'blob', self.response is a Blob. // cf. section 3.6, subsections 8,9,10,11 of https://xhr.spec.whatwg.org/#the-response-attribute this.responseType = ""; /* 'arraybuffer' or 'text' or '' or 'json' or 'blob' */ /** * Private methods */ /** * Check if the specified header is allowed. * * @param string header Header to validate * @return boolean False if not allowed, otherwise true */ var isAllowedHttpHeader = function(header) { return disableHeaderCheck || (header && forbiddenRequestHeaders.indexOf(header.toLowerCase()) === -1); }; /** * Check if the specified method is allowed. * * @param string method Request method to validate * @return boolean False if not allowed, otherwise true */ var isAllowedHttpMethod = function(method) { return (method && forbiddenRequestMethods.indexOf(method) === -1); }; /** * When xhr.responseType === 'arraybuffer', xhr.response must have type ArrayBuffer according * to section 3.6.9 of https://xhr.spec.whatwg.org/#the-response-attribute . * However, bufTotal = Buffer.concat(...) often has byteOffset > 0, so bufTotal.buffer is larger * than the useable region in bufTotal. This means that a new copy of bufTotal would need to be * created to get the correct ArrayBuffer. Instead, do the concat by hand to create the right * sized ArrayBuffer in the first place. * * The return type is Uint8Array, * because often Buffer will have Buffer.length < Buffer.buffer.byteLength. * * @param {Array} bufferArray * @returns {Uint8Array} */ var concat = function(bufferArray) { let length = 0, offset = 0; for (let k = 0; k < bufferArray.length; k++) length += bufferArray[k].length; const result = new Uint8Array(length); for (let k = 0; k < bufferArray.length; k++) { result.set(bufferArray[k], offset); offset += bufferArray[k].length; } return result; }; /** * When xhr.responseType === 'arraybuffer', xhr.response must have type ArrayBuffer according * to section 3.6.9 of https://xhr.spec.whatwg.org/#the-response-attribute . * However, buf = Buffer.from(str) often has byteOffset > 0, so buf.buffer is larger than the * usable region in buf. This means that a new copy of buf would need to be created to get the * correct arrayBuffer. Instead, do it by hand to create the right sized ArrayBuffer in the * first place. * * @param {string} str * @returns {Buffer} */ var stringToBuffer = function(str) { const ab = new ArrayBuffer(str.length) const buf = Buffer.from(ab); for (let k = 0; k < str.length; k++) buf[k] = Number(str.charCodeAt(k)); return buf; } /** * Given a Buffer buf, check whether buf.buffer.byteLength > buf.length and if so, * create a new ArrayBuffer whose byteLength is buf.length, containing the bytes. * of buf. This function shouldn't usually be needed, unless there's a future * behavior change where buf.buffer.byteLength > buf.length unexpectedly. * * @param {Buffer} buf * @returns {ArrayBuffer} */ var checkAndShrinkBuffer = function(buf) { if (buf.length === buf.buffer.byteLength) return buf.buffer; const ab = new ArrayBuffer(buf.length); const result = Buffer.from(ab); for (let k = 0; k < buf.length; k++) result[k] = buf[k]; return ab; } /** * Public methods */ /** * Open the connection. Currently supports local server requests. * * @param string method Connection method (eg GET, POST) * @param string url URL for the connection. * @param boolean async Asynchronous connection. Default is true. * @param string user Username for basic authentication (optional) * @param string password Password for basic authentication (optional) */ this.open = function(method, url, async, user, password) { this.abort(); errorFlag = false; abortedFlag = false; // Check for valid request method if (!isAllowedHttpMethod(method)) { throw new Error("SecurityError: Request method not allowed"); } settings = { "method": method.toUpperCase(), "url": url, "async": (typeof async !== "boolean" ? true : async), "user": user || null, "password": password || null }; // parse origin try { settings.origin = new URL(opts.origin); } catch (e) { settings.origin = null; } setState(this.OPENED); }; /** * Disables or enables isAllowedHttpHeader() check the request. Enabled by default. * This does not conform to the W3C spec. * * @param boolean state Enable or disable header checking. */ this.setDisableHeaderCheck = function(state) { disableHeaderCheck = state; }; /** * Sets a header for the request. * * @param string header Header name * @param string value Header value * @return boolean Header added */ this.setRequestHeader = function(header, value) { if (this.readyState != this.OPENED) { throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN"); } if (!isAllowedHttpHeader(header)) { console.warn('Refused to set unsafe header "' + header + '"'); return false; } if (sendFlag) { throw new Error("INVALID_STATE_ERR: send flag is true"); } headers[header] = value; return true; }; /** * Gets a header from the server response. * * @param string header Name of header to get. * @return string Text of the header or null if it doesn't exist. */ this.getResponseHeader = function(header) { // in case of local request, headers are not present if (typeof header === "string" && this.readyState > this.OPENED && response.headers[header.toLowerCase()] && !errorFlag && response && response.headers ) { return response.headers[header.toLowerCase()]; } return null; }; /** * Gets all the response headers. * * @return string A string with all response headers separated by CR+LF */ this.getAllResponseHeaders = function() { // in case of local request, headers are not present if (this.readyState < this.HEADERS_RECEIVED || errorFlag || !response || !response.headers) { return ""; } var result = ""; for (var i in response.headers) { // Cookie headers are excluded if (i !== "set-cookie" && i !== "set-cookie2") { result += i + ": " + response.headers[i] + "\r\n"; } } return result.slice(0, -2); }; /** * Gets a request header * * @param string name Name of header to get * @return string Returns the request header or empty string if not set */ this.getRequestHeader = function(name) { // @TODO Make this case insensitive if (typeof name === "string" && headers[name]) { return headers[name]; } return ""; }; /** * Convert from Data URI to Buffer * @param {URL} url URI to parse * @returns {Buffer} buffer */ var bufferFromDataUri = function (url) { // Triming from original url object for more consistency var data = url.href.slice(5); // separator between header and actual data var parts = data.split(",", 2); if (parts.length < 2) throw "Invalid URL"; // check if header part has base64 (from 2nd header onwards) var base64 = parts[0].split(";").some(function (dataHeader, index) { return index > 0 && dataHeader.toLowerCase() === "base64"; }); var responseData, inputData = decodeURIComponent(parts[1]); if (base64) { // remove any ASCII whitespaces inputData = inputData.replace(/(\s|\t|\r|\n|\v|\f)+/g, ""); // check padding amount let padding = inputData.match(/=*$/)[0]; if (padding.length + (inputData.length - padding.length) % 4 > 4) throw "invalid padding"; inputData = inputData.slice(0, inputData.length - padding.length); responseData = Buffer.from(inputData, "base64"); if (responseData.toString("base64").replace(/=+$/, "") !== inputData) throw "malformed base64 encoding"; return responseData; } else { return Buffer.from(inputData); } } /** * Sends the request to the server. * * @param string data Optional data to send as request body. */ this.send = function(data) { if (this.readyState != this.OPENED) { throw new Error("INVALID_STATE_ERR: connection must be opened before send() is called"); } if (sendFlag) { throw new Error("INVALID_STATE_ERR: send has already been called"); } var isSsl = false, isLocal = false, isDataUri = false; var url; try { if (settings.origin) { url = new URL(settings.url, settings.origin); } else { url = new URL(settings.url); } settings.url = url.href; } catch (e) { // URL parsing throws TypeError, here we only want to take its message self.handleError(new Error(e.message)); return; } var host; // Determine the server switch (url.protocol) { case 'https:': isSsl = true; // SSL & non-SSL both need host, no break here. case 'http:': host = url.hostname; break; case 'data:': isDataUri = true; case 'file:': isLocal = true; break; case undefined: case '': host = "localhost"; break; default: throw new Error("Protocol not supported."); } // Load files off the local filesystem (file://) // or data from Data URI (data:) if (isLocal) { if (isDataUri) try { self.status = 200; self.responseURL = settings.url; self.createFileOrSyncResponse(bufferFromDataUri(url)); setState(self.DONE); return; } catch (e) { self.handleError(new Error("Invalid data URI")); return; } if (!opts.allowFileSystemResources) { self.handleError(new Error("Not allowed to load local resource: " + url.href)); return; } if (settings.method !== "GET") { throw new Error("XMLHttpRequest: Only GET method is supported"); } if (settings.async) { fs.readFile(unescape(url.pathname), function(error, data) { if (error) { self.handleError(error, error.errno || -1); } else { self.status = 200; self.responseURL = settings.url; // Use self.responseType to create the correct self.responseType, self.response. self.createFileOrSyncResponse(data); setState(self.DONE); } }); } else { try { this.status = 200; const syncData = fs.readFileSync(unescape(url.pathname)); // Use self.responseType to create the correct self.responseType, self.response. this.responseURL = settings.url; this.createFileOrSyncResponse(syncData); setState(self.DONE); } catch(e) { this.handleError(e, e.errno || -1); } } return; } // Default to port 80. If accessing localhost on another port be sure // to use http://localhost:port/path var port = url.port || (isSsl ? 443 : 80); // Add query string if one is used var uri = url.pathname + (url.search || ''); // Set the Host header or the server may reject the request headers["Host"] = host; if (!((isSsl && port === 443) || port === 80)) { headers["Host"] += ':' + url.port; } // Set Basic Auth if necessary if (settings.user) { if (typeof settings.password === "undefined") { settings.password = ""; } var authBuf = Buffer.from(settings.user + ":" + settings.password); headers["Authorization"] = "Basic " + authBuf.toString("base64"); } // Set content length header if (settings.method === "GET" || settings.method === "HEAD") { data = null; } else if (data) { headers["Content-Length"] = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data); var headersKeys = Object.keys(headers); if (!headersKeys.some(function (h) { return h.toLowerCase() === 'content-type' })) { headers["Content-Type"] = "text/plain;charset=UTF-8"; } } else if (settings.method === "POST") { // For a post with no data set Content-Length: 0. // This is required by buggy servers that don't meet the specs. headers["Content-Length"] = 0; } var options = { host: host, port: port, path: uri, method: settings.method, headers: headers, agent: opts.agent || false }; // Reset error flag errorFlag = false; // Handle async requests if (settings.async) { // Use the proper protocol var doRequest = isSsl ? https.request : http.request; // Request is being sent, set send flag sendFlag = true; // As per spec, this is called here for historical reasons. self.dispatchEvent("readystatechange"); // Handler for the response var responseHandler = function(resp) { // Check for redirect if ( resp.statusCode === 301 || resp.statusCode === 302 || resp.statusCode === 303 || resp.statusCode === 307 || resp.statusCode === 308 ) { ++redirectCount; // end the response resp.destroy(); if (redirectCount > maxRedirects) { self.handleError(new Error("Too many redirects")); return; } // Change URL to the redirect location var url; try { url = new URL(resp.headers.location, settings.url); // reject redirects to any protocols other than http and https if (url.protocol !== "https:" && url.protocol !== "http:") throw "bad protocol"; settings.url = url.href; } catch (e) { self.handleError(new Error("Unsafe redirect")); return; } // change request options again to match with new redirect protocol isSsl = url.protocol === "https:"; doRequest = isSsl ? https.request : http.request; // Set host and port var in case it's used later host = url.hostname; port = url.port || (isSsl ? 443 : 80); headers["Host"] = host; if (!((isSsl && port === 443) || port === 80)) { headers["Host"] += ':' + url.port; } // Options for the new request var newOptions = { hostname: url.hostname, port: port, path: url.pathname + (url.search || ''), method: resp.statusCode === 303 ? 'GET' : settings.method, headers: headers }; // Issue the new request createRequest(newOptions); // @TODO Check if an XHR event needs to be fired here return; } // Set response var to the response we got back // This is so it remains accessable outside this scope response = resp; // Collect buffers and concatenate once. const buffers = []; setState(self.HEADERS_RECEIVED); // When responseType is 'text' or '', self.responseText will be utf8 decoded text. // When responseType is 'json', self.responseText initially will be utf8 decoded text, // which is then JSON parsed into self.response. // When responseType is 'arraybuffer', self.response is an ArrayBuffer. // When responseType is 'blob', self.response is a Blob. // cf. section 3.6, subsections 8,9,10,11 of https://xhr.spec.whatwg.org/#the-response-attribute const isUtf8 = self.responseType === "" || self.responseType === "text" || self.responseType === "json"; if (isUtf8 && response.setEncoding) { response.setEncoding("utf8"); } self.status = response.statusCode; response.on('data', function(chunk) { // Make sure there's some data if (chunk) { if (isUtf8) { // When responseType is 'text', '', 'json', // then each chunk is already utf8 decoded. self.responseText += chunk; } else { // Otherwise collect the chunk buffers. buffers.push(chunk); } } // Don't emit state changes if the connection has been aborted. if (sendFlag) { setState(self.LOADING); } }.bind(response)); response.on('end', function() { if (sendFlag) { // The sendFlag needs to be set before setState is called. Otherwise if we are chaining callbacks // there can be a timing issue (the callback is called and a new call is made before the flag is reset). sendFlag = false; // Create the correct response for responseType. self.createResponse(buffers); self.statusText = this.statusMessage; self.responseURL = settings.url; // Discard the 'end' event if the connection has been aborted setState(self.DONE); } }.bind(response)); response.on('error', function(error) { self.handleError(error); }.bind(response)); } // Error handler for the request var errorHandler = function(error) { // In the case of https://nodejs.org/api/http.html#requestreusedsocket triggering an ECONNRESET, // don't fail the xhr request, attempt again. if (request.reusedSocket && error.code === 'ECONNRESET') return doRequest(options, responseHandler).on('error', errorHandler); self.handleError(error); } var createRequest = function (opt) { opt = Object.assign({}, opt); if (isSsl) Object.assign(opt, sslOptions); request = doRequest(opt, responseHandler).on('error', errorHandler); if (opts.autoUnref) { request.on('socket', function (socket) { socket.unref(); }); } // Node 0.4 and later won't accept empty data. Make sure it's needed. if (data) { request.write(data); } request.end(); } // Create the request createRequest(options); self.dispatchEvent("loadstart"); } else { // Synchronous // Create a temporary file for communication with the other Node process var contentFile = ".node-xmlhttprequest-content-" + process.pid; var syncFile = ".node-xmlhttprequest-sync-" + process.pid; fs.writeFileSync(syncFile, "", "utf8"); // The async request the other Node process executes var execString = "'use strict';" + "var http = require('http'), https = require('https'), fs = require('fs');" + "function concat(bufferArray) {" + " let length = 0, offset = 0;" + " for (let k = 0; k < bufferArray.length; k++)" + " length += bufferArray[k].length;" + " const result = Buffer.alloc(length);" + " for (let k = 0; k < bufferArray.length; k++) {" + " for (let i = 0; i < bufferArray[k].length; i++) {" + " result[offset+i] = bufferArray[k][i]" + " }" + " offset += bufferArray[k].length;" + " }" + " return result;" + "};" + "var doRequest = http" + (isSsl ? "s" : "") + ".request;" + "var isSsl = " + !!isSsl + ";" + "var options = " + JSON.stringify(options) + ";" + "var sslOptions = " + JSON.stringify(sslOptions) + ";" + "var responseData = Buffer.alloc(0);" + "var buffers = [];" + "var url = new URL(" + JSON.stringify(settings.url) + ");" + "var maxRedirects = " + maxRedirects + ", redirects_count = 0;" + "var makeRequest = function () {" + " var opt = Object.assign({}, options);" + " if (isSsl) Object.assign(opt, sslOptions);" + " var req = doRequest(opt, function(response) {" + " if (response.statusCode === 301 || response.statusCode === 302 || response.statusCode === 303 || response.statusCode === 307 || response.statusCode === 308) {" + " response.destroy();" + " ++redirects_count;" + " if (redirects_count > maxRedirects) {" + " fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR-REDIRECT: Too many redirects', 'utf8');" + " fs.unlinkSync('" + syncFile + "');" + " return;" + " }" + " try {" + " url = new URL(response.headers.location, url);" + " if (url.protocol !== 'https:' && url.protocol !== 'http:') throw 'bad protocol';" + " }" + " catch (e) {" + " fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR-REDIRECT: Unsafe redirect', 'utf8');" + " fs.unlinkSync('" + syncFile + "');" + " return;" + " };" + " isSsl = url.protocol === 'https:';" + " doRequest = isSsl ? https.request : http.request;" + " var port = url.port;" + " options = {" + " hostname: url.hostname," + " port: port," + " path: url.pathname + (url.search || '')," + " method: response.statusCode === 303 ? 'GET' : options.method," + " headers: options.headers" + " };" + " options.headers['Host'] = url.host;" + " if (!((isSsl && port === 443) || port === 80)) options.headers['Host'] += ':' + port;" + " makeRequest();" + " return;" + " }" + " response.on('data', function(chunk) {" + " buffers.push(chunk);" + " });" + " response.on('end', function() {" + " responseData = concat(buffers);" + " fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {url: url.href, statusCode: response.statusCode, statusText: response.statusMessage, headers: response.headers, data: responseData.toString('utf8')}}), 'utf8');" + " fs.unlinkSync('" + syncFile + "');" + " });" + " response.on('error', function(error) {" + " fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" + " fs.unlinkSync('" + syncFile + "');" + " });" + " }).on('error', function(error) {" + " fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');" + " fs.unlinkSync('" + syncFile + "');" + " });" + " " + (data ? "req.write('" + JSON.stringify(data).slice(1,-1).replace(/'/g, "\\'") + "');":"") + " req.end();" + "};" + "makeRequest();" // Start the other Node Process, executing this string var syncProc = spawn(process.argv[0], ["-e", execString]); while(fs.existsSync(syncFile)) { // Wait while the sync file is empty } self.responseText = fs.readFileSync(contentFile, 'utf8'); // Kill the child process once the file has data syncProc.stdin.end(); // Remove the temporary file fs.unlinkSync(contentFile); if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR(-REDIRECT){0,1}:/)) { // If the file returned an error, handle it if (self.responseText.startsWith('NODE-XMLHTTPREQUEST-ERROR-REDIRECT')) { self.handleError(new Error(self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR-REDIRECT: /, ""))); } else { var errorObj = JSON.parse(self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, "")); self.handleError(errorObj, 503); } } else { // If the file returned okay, parse its data and move to the DONE state const resp = JSON.parse(self.responseText); self.status = resp.data.statusCode; self.statusText = resp.data.statusText; self.responseURL = resp.data.url; self.response = stringToBuffer(resp.data.data); // Use self.responseType to create the correct self.responseType, self.response, self.responseXML. self.createFileOrSyncResponse(self.response); // Set up response correctly. response = { statusCode: self.status, headers: resp.data.headers }; setState(self.DONE); } } }; /** * Called when an error is encountered to deal with it. * @param status {number} HTTP status code to use rather than the default (0) for XHR errors. */ this.handleError = function(error, status) { this.status = status || 0; this.statusText = error.message || ""; this.responseText = ""; this.responseXML = ""; this.responseURL = ""; this.response = Buffer.alloc(0); errorFlag = true; setState(this.DONE); if (!settings.async) throw error; }; /** * Aborts a request. */ this.abort = function() { if (request) { request.abort(); request = null; } headers = Object.assign({}, defaultHeaders); this.responseText = ""; this.responseXML = ""; this.response = Buffer.alloc(0); errorFlag = abortedFlag = true if (this.readyState !== this.UNSENT && (this.readyState !== this.OPENED || sendFlag) && this.readyState !== this.DONE) { sendFlag = false; setState(this.DONE); } this.readyState = this.UNSENT; }; /** * Adds an event listener. Preferred method of binding to events. */ this.addEventListener = function(event, callback) { if (!(event in listeners)) { listeners[event] = []; } // Currently allows duplicate callbacks. Should it? listeners[event].push(callback); }; /** * Remove an event callback that has already been bound. * Only works on the matching funciton, cannot be a copy. */ this.removeEventListener = function(event, callback) { if (event in listeners) { // Filter will return a new array with the callback removed listeners[event] = listeners[event].filter(function(ev) { return ev !== callback; }); } }; /** * Dispatch any events, including both "on" methods and events attached using addEventListener. */ this.dispatchEvent = function (event) { let argument = { type: event }; if (typeof self["on" + event] === "function") { if (this.readyState === this.DONE && settings.async) setTimeout(function() { self["on" + event](argument) }, 0) else self["on" + event](argument) } if (event in listeners) { for (let i = 0, len = listeners[event].length; i < len; i++) { if (this.readyState === this.DONE) setTimeout(function() { listeners[event][i].call(self, argument) }, 0) else listeners[event][i].call(self, argument) } } }; /** * Construct the correct form of response, given responseType when in non-file based, asynchronous mode. * * When self.responseType is "", "text", "json", self.responseText is a utf8 string. * When self.responseType is "arraybuffer", "blob", the response is in the buffers parameter, * an Array of Buffers. Then concat(buffers) is Uint8Array, from which checkAndShrinkBuffer * extracts the correct sized ArrayBuffer. * * @param {Array} buffers */ this.createResponse = function(buffers) { self.responseXML = ''; switch (self.responseType) { case "": case "text": self.response = self.responseText; break; case 'json': self.response = JSON.parse(self.responseText); self.responseText = ''; break; default: self.responseText = ''; const totalResponse = concat(buffers); // When self.responseType === 'arraybuffer', self.response is an ArrayBuffer. // Get the correct sized ArrayBuffer. self.response = checkAndShrinkBuffer(totalResponse); if (self.responseType === 'blob' && typeof Blob === 'function') { // Construct the Blob object that contains response. self.response = new Blob([self.response]); } break; } } /** * Construct the correct form of response, given responseType when in synchronous mode or file based. * * The input is the response parameter which is a Buffer. * When self.responseType is "", "text", "json", * the input is further refined to be: response.toString('utf8'). * When self.responseType is "arraybuffer", "blob", * the input is further refined to be: checkAndShrinkBuffer(response). * * @param {Buffer} response */ this.createFileOrSyncResponse = function(response) { self.responseText = ''; self.responseXML = ''; switch (self.responseType) { case "": case "text": self.responseText = response.toString('utf8'); self.response = self.responseText; break; case 'json': self.response = JSON.parse(response.toString('utf8')); break; default: // When self.responseType === 'arraybuffer', self.response is an ArrayBuffer. // Get the correct sized ArrayBuffer. self.response = checkAndShrinkBuffer(response); if (self.responseType === 'blob' && typeof Blob === 'function') { // Construct the Blob object that contains response. self.response = new Blob([self.response]); } break; } } /** * Changes readyState and calls onreadystatechange. * * @param int state New state */ var setState = function(state) { if ((self.readyState === state) || (self.readyState === self.UNSENT && abortedFlag)) return self.readyState = state; if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) { self.dispatchEvent("readystatechange"); } if (self.readyState === self.DONE) { let fire if (abortedFlag) fire = "abort" else if (errorFlag) fire = "error" else fire = "load" self.dispatchEvent(fire) // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie) self.dispatchEvent("loadend"); } }; }; node-XMLHttpRequest-master/package.json000066400000000000000000000014221472526051200204340ustar00rootroot00000000000000{ "name": "xmlhttprequest-ssl", "description": "XMLHttpRequest for Node", "version": "3.1.0", "author": { "name": "Michael de Wit" }, "keywords": [ "xhr", "ajax" ], "licenses": [ { "type": "MIT", "url": "http://creativecommons.org/licenses/MIT/" } ], "repository": { "type": "git", "url": "git://github.com/mjwwit/node-XMLHttpRequest.git" }, "bugs": "http://github.com/mjwwit/node-XMLHttpRequest/issues", "engines": { "node": ">=12.0.0" }, "scripts": { "test": "cd ./tests && node run-test.js" }, "directories": { "lib": "./lib", "example": "./example" }, "files": [ "lib/XMLHttpRequest.js", "LICENSE", "README.md" ], "main": "./lib/XMLHttpRequest.js", "dependencies": {} } node-XMLHttpRequest-master/tests/000077500000000000000000000000001472526051200173115ustar00rootroot00000000000000node-XMLHttpRequest-master/tests/run-test.js000066400000000000000000000024031472526051200214270ustar00rootroot00000000000000var ignored_files = [ "run-test.js", // this file "server.js" ]; var spawnSync = require("child_process").spawnSync; var fs = require("fs"); var path = require("path"); // global flag to check if some of test fails, and will store location of failed test file var fail_path = false; // function to read and conduct test case var run_test = function (file) { if (fail_path) return; // logging console.log("Running:", file); // spawn a nodejs process var proc = spawnSync("node", [file]); if (proc.status === 0) { console.log(proc.stdout.toString()); console.log("--> PASSED"); } else { fail_path = file; console.log("--> TEST FAILED - CAUGHT ERROR:", proc.stderr.toString()); } } var check_dir = function (dirPath) { if (fail_path) return; var files = fs.readdirSync(dirPath); for (var file of files) { // return early in case something fails if (fail_path) return; var full_path = path.join(dirPath, file); if (fs.statSync(full_path).isDirectory()) check_dir(full_path); else if (path.extname(file) === ".js" && !ignored_files.includes(full_path)) run_test(full_path); } } // start test check_dir("./"); if (fail_path) throw new Error("Test failed at file: " + fail_path); console.log("ALL TESTS PASSED."); node-XMLHttpRequest-master/tests/server.js000066400000000000000000000037331472526051200211630ustar00rootroot00000000000000'use strict'; var http = require("http"); var server = http.createServer(function (req, res) { switch (req.url) { case "/": { var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.end(body); return; } case "/text": res.writeHead(200, {"Content-Type": "text/plain"}) res.end("Hello world!"); return; case "/xml": res.writeHead(200, {"Content-Type": "application/xml"}) res.end("Foobar"); return; case "/json": res.writeHead(200, {"Content-Type": "application/json"}) res.end(JSON.stringify({ foo: "bar" })); return; case "/binary1": res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(Buffer.from("Hello world!")); return; case "/binary2": const ta = new Float32Array([1, 5, 6, 7]); const buf = Buffer.from(ta.buffer); const str = buf.toString('binary'); res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(str); return; default: if (req.url.startsWith('/redirectingResource/')) { let remaining = req.url.replace(/^\/redirectingResource\/*/, "") - 1; res.writeHead(301, {'Location': remaining ? ('http://localhost:8888/redirectingResource/' + remaining) : 'http://localhost:8888/'}); res.end(); } else { res.writeHead(404, {"Content-Type": "text/plain"}) res.end("Not found"); } } }).listen(8888); process.on("SIGINT", function () { server.close(); }); node-XMLHttpRequest-master/tests/test-constants.js000066400000000000000000000005151472526051200226410ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest(); // Test constant values assert.equal(0, xhr.UNSENT); assert.equal(1, xhr.OPENED); assert.equal(2, xhr.HEADERS_RECEIVED); assert.equal(3, xhr.LOADING); assert.equal(4, xhr.DONE); console.log("done"); node-XMLHttpRequest-master/tests/test-data-uri.js000066400000000000000000000061311472526051200223330ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr; xhr = new XMLHttpRequest(); // define test data var tests = [ { name: "Test plain URI Data", data: "data:,Hello%20World", output: "Hello World" }, { name: "Test plain URI Data with spaces", data: "data:, Hello World", output: " Hello World" }, { name: "Test plain URI Data with data URI headers", data: "data:base64;example=1;args=2,Hello%20World", output: "Hello World" }, { name: "Test normal bass64-encoded data URI", data: "data:text;base64,SGVsbG8gV29ybGQ=", output: "Hello World" }, { name: "Test normal bass64-encoded data URI with mixed space characters", data: "data:text;base64,SGV sbG8gV\n29ybGQ=", output: "Hello World" }, { name: "Test normal bass64-encoded data URI with mixed space characters (url-encoded)", data: "data:text;base64,SGV%20sbG8gV%0a29ybGQ=", output: "Hello World" }, { name: "Test normal bass64-encoded data URI with invalid characters", data: "data:text;base64,SGV&&&&sbG8gV{29ybGQ=", error: "Invalid data URI" }, { name: "Test normal bass64-encoded data URI with invalid characters (url-encoded)", data: "data:text;base64,SGV%26%26%26%26sbG8gV%7B29ybGQ%3D", error: "Invalid data URI" }, { name: "Test base64-encoded data with no paddings", data: "data:text;base64,SGVsbG8gV29ybGQ", output: "Hello World" }, { name: "Test base64-encoded data with excessive paddings", data: "data:text;base64,SGVsbG8gV29ybGQ==", error: "Invalid data URI" } ]; var tests_passed = 0; var runAsyncTest = function (test) { console.log(" ASYNC"); xhr = new XMLHttpRequest; xhr.open("get", test.data); xhr.onreadystatechange = function () { if (this.readyState === 4) { if (test.error) { assert.equal(xhr.status, 0); assert.equal(xhr.statusText, test.error); } else { assert.equal(xhr.status, 200); assert.equal(xhr.responseText, test.output); } console.log(" --> SUCESS"); ++tests_passed; } } xhr.send(); } var runSyncTest = function (test) { console.log(" SYNC"); xhr = new XMLHttpRequest; xhr.open("get", test.data, false); try { xhr.send(); if (test.error) throw "Expected to fail, Success with " + e.responseText; assert.equal(xhr.status, 200); assert.equal(xhr.responseText, test.output); } catch (e) { if (!test.error) throw "Expected to success, Caught error: " + e.toString() assert.equal(xhr.status, 0); assert.equal(e.message, test.error); } console.log(" --> SUCCESS"); ++tests_passed; } var i = 0; var startTest = function () { let test = tests[i]; if (!test) { console.log("Done:", tests_passed === tests.length * 2 ? "PASS" : "FAILED"); return; } console.log(test.name); runAsyncTest(test); setTimeout(function () { try { runSyncTest(test); } catch (e) { console.error(e); throw e; }; console.log(""); ++i; startTest(); }, 500); } startTest(); node-XMLHttpRequest-master/tests/test-disallow-fs-resources.js000066400000000000000000000017031472526051200250610ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr; xhr = new XMLHttpRequest({ allowFileSystemResources: false }); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(this.statusText, "Not allowed to load local resource: " + url); assert.equal(this.status, 0); try { runSync(); } catch (e) { if (e instanceof assert.AssertionError) { throw e; } } } }; // Async var url = "file://" + __dirname + "/testdata.txt"; xhr.open("GET", url); xhr.send(); // Sync var runSync = function() { xhr = new XMLHttpRequest({ allowFileSystemResources: false }); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(this.statusText, "Not allowed to load local resource: " + url); assert.equal(this.status, 0); console.log("done"); } }; xhr.open("GET", url, false); xhr.send(); } node-XMLHttpRequest-master/tests/test-events.js000066400000000000000000000023021472526051200221250ustar00rootroot00000000000000var assert = require("assert") , http = require("http") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr; // Test server var server = http.createServer(function (req, res) { var body = (req.method != "HEAD" ? "Hello World" : ""); res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body) }); // HEAD has no body if (req.method != "HEAD") { res.write(body); } res.end(); assert.equal(onreadystatechange, true); assert.equal(readystatechange, true); assert.equal(removed, true); console.log("done"); this.close(); }).listen(8000); xhr = new XMLHttpRequest(); // Track event calls var onreadystatechange = false; var readystatechange = false; var removed = true; var removedEvent = function() { removed = false; }; xhr.onreadystatechange = function() { onreadystatechange = true; }; xhr.addEventListener("readystatechange", function() { readystatechange = true; }); // This isn't perfect, won't guarantee it was added in the first place xhr.addEventListener("readystatechange", removedEvent); xhr.removeEventListener("readystatechange", removedEvent); xhr.open("GET", "http://localhost:8000"); xhr.send(); node-XMLHttpRequest-master/tests/test-exceptions.js000066400000000000000000000026001472526051200230030ustar00rootroot00000000000000var XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest(); // Test request methods that aren't allowed try { xhr.open("TRACK", "http://localhost:8000/"); console.log("ERROR: TRACK should have thrown exception"); } catch(e) {} try { xhr.open("TRACE", "http://localhost:8000/"); console.log("ERROR: TRACE should have thrown exception"); } catch(e) {} try { xhr.open("CONNECT", "http://localhost:8000/"); console.log("ERROR: CONNECT should have thrown exception"); } catch(e) {} // Test valid request method try { xhr.open("GET", "http://localhost:8000/"); } catch(e) { console.error(e); throw new Error("ERROR: Invalid exception for GET"); } // Test forbidden headers var forbiddenRequestHeaders = [ "accept-charset", "accept-encoding", "access-control-request-headers", "access-control-request-method", "connection", "content-length", "content-transfer-encoding", "cookie", "cookie2", "date", "expect", "host", "keep-alive", "origin", "referer", "te", "trailer", "transfer-encoding", "upgrade", "via" ]; for (var i in forbiddenRequestHeaders) { if(xhr.setRequestHeader(forbiddenRequestHeaders[i], "Test") !== false) { throw new Error("ERROR: " + forbiddenRequestHeaders[i] + " should have thrown exception"); } } // Try valid header xhr.setRequestHeader("X-Foobar", "Test"); console.log("Done"); node-XMLHttpRequest-master/tests/test-headers.js000066400000000000000000000053751472526051200222510ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { // Test setRequestHeader assert.equal("Foobar", req.headers["x-test"]); // Test non-conforming allowed header assert.equal("node-XMLHttpRequest-test", req.headers["user-agent"]); // Test header set with blacklist disabled assert.equal("http://github.com", req.headers["referer"]); // Test case insensitive header was set assert.equal("text/plain", req.headers["content-type"]); var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), // Set cookie headers to see if they're correctly suppressed // Actual values don't matter "Set-Cookie": "foo=bar", "Set-Cookie2": "bar=baz", "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState == 4) { // Test getAllResponseHeaders() var headers = "content-type: text/plain\r\ncontent-length: 11\r\ndate: Thu, 30 Aug 2012 18:17:53 GMT\r\nconnection: close"; assert.equal(headers, this.getAllResponseHeaders()); // Test case insensitivity assert.equal('text/plain', this.getResponseHeader('Content-Type')); assert.equal('text/plain', this.getResponseHeader('Content-type')); assert.equal('text/plain', this.getResponseHeader('content-Type')); assert.equal('text/plain', this.getResponseHeader('content-type')); // Test aborted getAllResponseHeaders this.abort(); assert.equal("", this.getAllResponseHeaders()); assert.equal(null, this.getResponseHeader("Connection")); console.log("done"); } }; assert.equal(null, xhr.getResponseHeader("Content-Type")); try { xhr.open("POST", "http://localhost:8000/"); var body = "Hello World"; // Valid header xhr.setRequestHeader("X-Test", "Foobar"); // Invalid header xhr.setRequestHeader("Content-Length", Buffer.byteLength(body)); // Allowed header outside of specs xhr.setRequestHeader("user-agent", "node-XMLHttpRequest-test"); // Case insensitive header xhr.setRequestHeader("content-type", 'text/plain'); // Test getRequestHeader assert.equal("Foobar", xhr.getRequestHeader("X-Test")); // Test invalid header assert.equal("", xhr.getRequestHeader("Content-Length")); // Test allowing all headers xhr.setDisableHeaderCheck(true); xhr.setRequestHeader("Referer", "http://github.com"); assert.equal("http://github.com", xhr.getRequestHeader("Referer")); xhr.send(body); } catch(e) { console.error("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-keepalive.js000066400000000000000000000017241472526051200225750ustar00rootroot00000000000000var assert = require("assert"); var http = require('http'); var { XMLHttpRequest } = require("../lib/XMLHttpRequest"); var server = http.createServer({ keepAliveTimeout: 200 }, function handleConnection (req, res) { res.write('hello\n'); res.end(); }).listen(8889); var agent = new http.Agent({ keepAlive: true, keepAliveMsecs: 2000, }); var xhr = new XMLHttpRequest({ agent }); var url = "http://localhost:8889"; var repeats = 0; var maxMessages = 20; var interval = setInterval(function sendRequest() { xhr.open("GET", url); xhr.onloadend = function(event) { if (xhr.status !== 200) { console.error('Error: non-200 xhr response, message is\n', xhr.responseText); clearInterval(interval); agent.destroy(); server.close(); assert.equal(xhr.status, 200); } if (repeats++ > maxMessages) { console.log('Done.'); clearInterval(interval); agent.destroy(); server.close(); } } xhr.send(); }, 200);node-XMLHttpRequest-master/tests/test-max-redirects.js000066400000000000000000000025041472526051200233740ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , spawn = require('child_process').spawn; // Test server var serverProcess = spawn(process.argv[0], [__dirname + "/server.js"], { stdio: 'inherit' }); var runTest = function () { try { let xhr = new XMLHttpRequest({ maxRedirects: 10 }); xhr.open("GET", "http://localhost:8888/redirectingResource/10", false); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("safe redirects count: done"); } }; xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } try { let xhr = new XMLHttpRequest({ maxRedirects: 10 }); xhr.open("GET", "http://localhost:8888/redirectingResource/20", false); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { assert.equal(xhr.statusText, 'Too many redirects'); assert.equal(xhr.status, 0); console.log("excessive redirects count: done"); } }; xhr.send(); } catch(e) { assert.equal(e.message, 'Too many redirects'); } } setTimeout(function () { try { runTest(); } finally { serverProcess.kill('SIGINT'); } }, 100); node-XMLHttpRequest-master/tests/test-perf.js000066400000000000000000000152541472526051200215670ustar00rootroot00000000000000 /****************************************************************************************** * This test measurs the elapsed time to download a Float32Array of length 100,000,000. */ 'use strict'; const http = require("http"); const XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest; const supressConsoleOutput = false; function log (_) { if ( !supressConsoleOutput) console.log.apply(console, arguments); } var serverProcess; /****************************************************************************************** * This section has various utility functions: * 1) Create a random Float32Array of length N. * 2) Efficiently concatenate the input Array of Buffers. */ /** * Create a random Float32Array of length N. * @param {number} N * @returns {Float32Array} */ function createFloat32Array (N) { let ta = new Float32Array(N); for (let k = 0; k < ta.length; k++) ta[k] = Math.random(); return ta; } /** * Efficiently concatenate the input Array of Buffers. * Why not use Buffer.concat(...) ? * Because bufTotal = Buffer.concat(...) often has byteOffset > 0, so bufTotal.buffer * is larger than the useable region in bufTotal. * @param {Array} bufferArray * @returns */ function concat (bufferArray) { var length = 0, offset = 0, k; for (k = 0; k < bufferArray.length; k++) length += bufferArray[k].length; const result = Buffer.alloc(length); for (k = 0; k < bufferArray.length; k++) { result.set(bufferArray[k], offset); offset += bufferArray[k].length; } return result; }; /****************************************************************************************** * This section produces a web server that serves up anything uploaded. * The uploaded data is stored as values in a storage object, where the keys are the upload url suffixes. * E.g. storage['/F32'] === Buffer containing the corresponding upload. */ const storage = { ralph: [1,2] }; function storageLength () { const result = {}; for (const key in storage) result[key] = storage[key].length; return result; } function checkStorage () { log('storage:', JSON.stringify(storageLength())); } /** * mini-webserver: Serves up anything uploaded. * Tested with: * const urlXml = "http://localhost:8888/Xml"; */ function createServer() { serverProcess = http.createServer(function (req, res) { req.on('error', err => { console.error('request:', err) }); res.on('error', err => { console.error('response:', err) }); if (req.method === 'POST') { const chunks = []; req.on('data', (chunk) => chunks.push(chunk)); req.on('end', () => { const u8 = concat(chunks); storage[req.url] = u8; // console.log('server end-handler', req.url, u8.length, req.headers); res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(`success:len ${u8.length}`); }); } else { if (!storage[req.url]) { res.writeHead(404, {"Content-Type": "text/plain; charset=utf8"}) res.end("Not in storage"); } else { res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(storage[req.url]); } } }).listen(8888); process.on("SIGINT", function () { if (serverProcess) serverProcess.close(); serverProcess = null; }); } createServer(); /****************************************************************************************** * This section creates: * 1) An upload function that POSTs using xmlhttprequest-ssl. * 2) A download function that GETs using xmlhttprequest-ssl and allows sepcifying xhr.responseType. */ function upload(xhr, url, data) { return new Promise((resolve, reject) => { xhr.open("POST", url, true); xhr.onloadend = () => { if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.responseText); else { const errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.setRequestHeader('Content-Type', 'multipart/form-data'); // Unnecessary. xhr.send(data); }); } function download (xhr, url, responseType) { responseType = responseType || 'arraybuffer'; return new Promise((resolve, reject) => { xhr.open("GET", url, true); xhr.responseType = responseType; xhr.onloadend = () => { if (xhr.status >= 200 && xhr.status < 300) { switch (responseType) { case "": case "text": resolve(xhr.responseText); break; case "document": resolve(xhr.responseXML); break; default: resolve(xhr.response); break; } } else { const errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.send(); }); } /****************************************************************************************** * This section: * 1) Uploads random float32 array array of length 100,000,000. . * 2) Downloads the float32 array and measures the download elpased time. */ const N = 100 * 1000 * 1000; const _f32 = createFloat32Array(N); const F32 = Buffer.from(_f32.buffer); const urlF32 = "http://localhost:8888/F32"; const xhr = new XMLHttpRequest(); var handle, success, _t0; /** * 1) Upload Float32Array of length N=100,000,000. * Then download using xhr.responseType="arraybuffer" and check the the array lengths are the same. */ function runTest() { let r = upload(xhr, urlF32, F32); // big return r.then(afterUpload) } function afterUpload(r) { log('upload urlF32, F32 ', r); log('-----------------------------------------------------------------------------------'); checkStorage(); // Check what's in the mini-webserver storage. log('-----------------------------------------------------------------------------------'); _t0 = Date.now(); success = true; handle = setTimeout(() => { console.error('Download has taken longer than 5 seconds and hence it has failed!'); success = false; }, 5 * 1000) const ab = download(xhr, urlF32, 'arraybuffer'); // big return ab.then(afterDownload); } function afterDownload(ab) { clearTimeout(handle); console.log(`Download elapsed time:, ${Date.now() - _t0}ms`, ab.byteLength); console.info('...waiting to see elapsed time of download...'); if (!success) throw new Error("Download has taken far too long!"); } /** * Run the test. * If runTest() fails, an exception will be thrown. */ setTimeout(function () { runTest() .then(() => { console.log("PASSED"); shutdown(); }) .catch((e) => { console.log("FAILED", e); shutdown(); throw e; }); }, 100); function shutdown() { if (serverProcess) serverProcess.close(); serverProcess = null; } node-XMLHttpRequest-master/tests/test-redirect-301.js000066400000000000000000000017711472526051200227340ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(301, {'Location': 'http://localhost:8000/'}); res.end(); return; } var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("done"); } }; try { xhr.open("GET", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-redirect-302.js000066400000000000000000000017711472526051200227350ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(302, {'Location': 'http://localhost:8000/'}); res.end(); return; } var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("done"); } }; try { xhr.open("GET", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-redirect-303.js000066400000000000000000000017721472526051200227370ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(303, {'Location': 'http://localhost:8000/'}); res.end(); return; } var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("done"); } }; try { xhr.open("POST", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-redirect-307.js000066400000000000000000000020371472526051200227360ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(307, {'Location': 'http://localhost:8000/'}); res.end(); return; } assert.equal(req.method, 'POST'); var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("done"); } }; try { xhr.open("POST", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-redirect-308.js000066400000000000000000000017711472526051200227430ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(308, {'Location': 'http://localhost:8000/'}); res.end(); return; } var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.getRequestHeader('Location'), ''); assert.equal(xhr.responseText, "Hello World"); console.log("done"); } }; try { xhr.open("GET", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-request-methods.js000066400000000000000000000026401472526051200237570ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , http = require("http") , xhr; // Test server var server = http.createServer(function (req, res) { // Check request method and URL assert.equal(methods[curMethod], req.method); assert.equal("/" + methods[curMethod], req.url); var body = (req.method != "HEAD" ? "Hello World" : ""); res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body) }); // HEAD has no body if (req.method != "HEAD") { res.write(body); } res.end(); if (curMethod == methods.length - 1) { this.close(); console.log("done"); } }).listen(8000); // Test standard methods var methods = ["GET", "POST", "HEAD", "PUT", "DELETE"]; var curMethod = 0; function start(method) { // Reset each time xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4) { if (method == "HEAD") { assert.equal("", this.responseText); } else { assert.equal("Hello World", this.responseText); } curMethod++; if (curMethod < methods.length) { console.log("Testing " + methods[curMethod]); start(methods[curMethod]); } } }; var url = "http://localhost:8000/" + method; xhr.open(method, url); xhr.send(); } console.log("Testing " + methods[curMethod]); start(methods[curMethod]); node-XMLHttpRequest-master/tests/test-request-protocols-binary-data.js000066400000000000000000000047711472526051200265400ustar00rootroot00000000000000/** * Test GET file URL with both async and sync mode. * Use xhr.responseType = "arraybuffer". */ 'use strict'; var XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest const supressConsoleOutput = true; function log (_) { if ( !supressConsoleOutput) console.log(arguments); } var url = "file://" + __dirname + "/testBinaryData"; function download (url, isAsync) { if (isAsync === undefined) isAsync = true; var xhr = new XMLHttpRequest(); return new Promise((resolve, reject) => { xhr.open("GET", url, true); xhr.responseType = 'arraybuffer'; xhr.onloadend = () => { if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response); else { const errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.send(); }); } function runTest () { // Async var ab = download(url, /*isAsync*/ true); return ab.then(afterAsyncDownload); } function afterAsyncDownload(ab) { var str = Buffer.from(ab).toString('binary'); var strLog = logBinary(str); log('async phase', strLog); if ("0000 803f 0000 a040 0000 c040 0000 e040" !== strLog) throw new Error(`Failed test-request-protocols-binary-data async phase: "0000 803f 0000 a040 0000 c040 0000 e040" !== ${strLog}`); log("done async phase"); // Sync var abSync = download(url, /*isAsync*/ false); return abSync.then(afterSyncDownload); } function afterSyncDownload(abSync) { var str = Buffer.from(abSync).toString('binary'); var strLog = logBinary(str); log('sync phase', strLog); if ("0000 803f 0000 a040 0000 c040 0000 e040" !== strLog) throw new Error(`Failed test-request-protocols-binary-data sync phase: "0000 803f 0000 a040 0000 c040 0000 e040" !== ${strLog}`); log("done sync phase"); } runTest() .then(() => console.log('PASSED')) .catch((e) => { console.error('FAILED'); throw e; }); function logBinary(data) { function log(data, idx) { const char = data.charCodeAt(idx).toString(16); // node compatibility: padStart doesn't exist to make sure return is 2 characters if (char.length === 1) return '0' + char; else return char; } if (!data) return 'no data'; if (typeof data !== 'string') return 'not a string'; let str = ''; for (let k = 0; k < data.length - 2; k += 2) str += `${log(data, k)}${log(data, k+1)} `; if ((data.length % 2) == 0) str += `${log(data, data.length - 2)}${log(data, data.length - 1)}`; else str += `${log(data, data.length - 1)}`; return str; } node-XMLHttpRequest-master/tests/test-request-protocols-txt-data.js000066400000000000000000000012161472526051200260620ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr; xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4) { assert.equal("Hello World", this.responseText); runSync(); } }; // Async var url = "file://" + __dirname + "/testdata.txt"; xhr.open("GET", url); xhr.send(); // Sync var runSync = function() { xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (this.readyState == 4) { assert.equal("Hello World", this.responseText); console.log("done"); } }; xhr.open("GET", url, false); xhr.send(); } node-XMLHttpRequest-master/tests/test-response-type.js000066400000000000000000000327541472526051200234540ustar00rootroot00000000000000 /****************************************************************************************** * This test validates xhr.responseType as described by: * section 3.6, subsections 8,9,10,11 of https://xhr.spec.whatwg.org/#the-response-attribute * except xhr.responseType='document' is not yet supported. * * 1) Create a simple min-webserver using the node http module. * 2) Upload 2 different float32 arrays . * 3) Upload the utf8 encoding of the underlying in-memory representations of 1). * 4) Upload a stringified JSON object. * 5) Then these 5 different uploads are downloaded as xhr.reponseType varies over * [ "text", "", "arraybuffer", "blob", "json" ] * and then various checks verify that the downloaded content is the same as that uploaded. */ 'use strict'; const http = require("http"); const XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest; const supressConsoleOutput = true; function log (_) { if ( !supressConsoleOutput) console.log(arguments); } var serverProcess; /****************************************************************************************** * This section has various utility functions: * 1) Convert typed array to binary string identical to underlying in-memory representation. * 2) Convert string to typed array when the string is the in-memory representation of a Float32Array. * 3) Display the underlying in-memory representation of the input string data. * 4) Pause/sleep for t milliseconds. * 5) Create a random Float32Array of length N. * 6) Check to see if 2 array-like objects have the same elements. * 7) Efficiently concatenate the input Array of Buffers. */ /** * Create a string corresponding to the in-memory representation of typed array ta. * @param {{ buffer: ArrayBuffer, length: number }} ta * @returns {string} */ function typedArrayToString (ta) { const u8 = new Uint8Array(ta.buffer); return u8.reduce((acc, cur) => acc + String.fromCharCode(cur), ""); } /** * Assumes str is the in-memory representation of a Float32Array. * Relies on the fact that the char codes in str are all <= 0xFF. * Returns Float32Array corresponding to str. * * @param {string} str * @returns {Float32Array} */ function stringToFloat32Array (str) { const u8 = new Uint8Array(str.length); for (let k = 0; k < str.length; k++) u8[k] = Number(str.charCodeAt(k)); return new Float32Array(u8.buffer); } /** * Create a random Float32Array of length N. * @param {number} N * @returns {Float32Array} */ function createFloat32Array (N) { let ta = new Float32Array(N); for (let k = 0; k < ta.length; k++) ta[k] = Math.random(); return ta; } /** * Check to see if 2 array-like objects have the same elements. * @param {{ length: number }} ar1 * @param {{ length: number }} ar2 * @returns {boolean} */ function isEqual (ar1, ar2) { if (ar1.length !== ar2.length) return false; for (let k = 0; k < ar1.length; k++) if (ar1[k] !== ar2[k]) return false; return true; } /** * Efficiently concatenate the input Array of Buffers. * Why not use Buffer.concat(...) ? * Because bufTotal = Buffer.concat(...) often has byteOffset > 0, so bufTotal.buffer * is larger than the useable region in bufTotal. * @param {Array} bufferArray * @returns */ function concat (bufferArray) { var length = 0, offset = 0, k; for (k = 0; k < bufferArray.length; k++) length += bufferArray[k].length; const result = Buffer.alloc(length); for (k = 0; k < bufferArray.length; k++) { bufferArray[k].copy(result, offset, 0, bufferArray[k].length) offset += bufferArray[k].length; } return result; }; /****************************************************************************************** * This section produces a web server that serves up anything uploaded. * The uploaded data is stored as values in a storage object, where the keys are the upload url suffixes. * E.g. storage['/F32'] === Buffer containing the corresponding upload. */ const storage = { ralph: [1,2] }; function storageLength () { const result = {}; for (const key in storage) if (key !== '/Json') // json not stored when uploading, but is stored when retrieving, new key makes check fail result[key] = storage[key].length; return result; } function checkStorage () { log('-----------------------------------------------------------------------------------'); log('storage:', JSON.stringify(storageLength())); log('-----------------------------------------------------------------------------------'); } // Xml doc for testing responseType "document" const xmlDoc = '' +' test' +' ' +''; /** * Serves up anything uploaded. * Tested with: * const urlF32 = "http://localhost:8888/F32"; * const urlF32_2 = "http://localhost:8888/F32_2"; * const urlUtf8 = "http://localhost:8888/Utf8"; * const urlUtf8_2 = "http://localhost:8888/Utf8_2"; * const urlJson = "http://localhost:8888/Json"; * const urlXml = "http://localhost:8888/Xml"; */ function createServer() { serverProcess = http.createServer(function (req, res) { req.on('error', err => { console.error('request:', err) }); res.on('error', err => { console.error('response:', err) }); if (req.method === 'POST') { const chunks = []; //req.on('data', chunk => chunks.push(chunk)); req.on('data', chunk => { // console.log('foo', chunk.toString('utf8')); // console.log('bar', JSON.parse(chunk.toString('utf8'))); // console.log('bar', unescape(chunk.toString('utf8'))); chunks.push(chunk); }); req.on('end', () => { const u8 = concat(chunks); storage[req.url] = u8; // console.log('server end-handler', req.url, u8.length, req.headers); // console.log(u8.toString('utf8')); // console.log('-------------------'); // console.log(xmlDoc); res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(`success:len ${u8.length}`); }); } else { if (!storage[req.url]) { res.writeHead(404, {"Content-Type": "text/plain; charset=utf8"}) res.end("Not in storage"); return; } if (req.url === "/Utf8" || req.url === "/Utf8_2" || req.url === "/Json" || req.url === "/Xml") { res.writeHead(200, {"Content-Type": "text/plain; charset=utf8"}) res.end(storage[req.url].toString()); return; } res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(storage[req.url]); } }).listen(8888); process.on("SIGINT", function () { if (serverProcess) serverProcess.close(); serverProcess = null; }); } createServer(); /****************************************************************************************** * This section creates: * 1) An upload function that POSTs using xmlhttprequest-ssl. * 2) A download function that GETs using xmlhttprequest-ssl and allows sepcifying xhr.responseType. */ function upload(xhr, url, data) { return new Promise((resolve, reject) => { xhr.open("POST", url, true); xhr.onloadend = () => { if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.responseText); else { const errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.setRequestHeader('Content-Type', 'multipart/form-data'); // Unnecessary. xhr.send(data); }); } function download (xhr, url, responseType) { responseType = responseType || 'arraybuffer'; return new Promise((resolve, reject) => { xhr.open("GET", url, true); xhr.responseType = responseType; xhr.onloadend = () => { if (xhr.status >= 200 && xhr.status < 300) { switch (responseType) { case "": case "text": resolve(xhr.responseText); break; case "document": resolve(xhr.responseXML); break; default: resolve(xhr.response); break; } } else { const errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.send(); }); } /****************************************************************************************** * This section: * 1) Uploads 2 different float32 arrays . * 2) Uploads the utf8 encoding of the underlying in-memory representations of 1). * 3) Uploads a stringified JSON object. * 4) Then these 5 different uploads are downloaded as xhr.reponseType varies over * [ "text", "", "arraybuffer", "blob", "json" ] * and then various checks verify that the downloaded content is the same as that uploaded. */ const N = 1 * 1000 * 1000; const _f32 = createFloat32Array(N); const _f32_2 = new Float32Array([ 1, 5, 6, 7, 2, 8 ]); const F32 = Buffer.from(_f32.buffer); const F32_2 = Buffer.from(_f32_2.buffer); const F32Utf8 = Buffer.from(typedArrayToString(_f32), 'utf8'); const F32Utf8_2 = Buffer.from(typedArrayToString(_f32_2), 'utf8'); const urlF32 = "http://localhost:8888/F32"; const urlF32_2 = "http://localhost:8888/F32_2"; const urlUtf8 = "http://localhost:8888/Utf8"; const urlUtf8_2 = "http://localhost:8888/Utf8_2"; const urlJson = "http://localhost:8888/Json"; const xhr = new XMLHttpRequest(); const type = (o) => { return `type=${o && o.constructor && o.constructor.name}`; }; /** * 1) Upload Float32Array of length N=1,000,000. * Then download using xhr.responseType="arraybuffer" and check the the array lengths are the same. * 2) Convert the Float32Array of 1) into a string, utf8 encode it and upload it. * Then download using xhr.responseType="text" and check the the string length is the same as the * byteLength of the array in 1). Downloading as "text" decodes the utf8 into the original. * 3) Upload Float32Array([1, 5, 6, 7, 2, 8]). * Then download using xhr.responseType="blob", extract the contained arrayBuffer, view it as * a Float32Aray and check that the contents are identical. * 4) Convert the Float32Array of 3) into a string, utf8 encode it and upload it. * Then download using xhr.responseType="" and check the the string length is the same as the * byteLength of the array in 3). Downloading as "" decodes the utf8 into the original. * 5) Let testJson be the current mini-webserver storage object: * e.g. testJson = {ralph:2,'/F32':4000000,'/Utf8':5333575,'/F32_2':24,'/Utf8_2':28,'/Xml':56,'/Json':77} * Upload JSON.stringify(testJson) and download it using xhr.responseType="json" * Check that the objects are the same by comparing the strings after calling JSON.stringify. * 6) Did a test of xhr.responseType="document" using a simple xml example. */ function runTest() { const uploadPromises = []; var r; return upload(xhr, urlF32, F32) // upload float32 .then((r) => { log('upload urlF32, F32 ', r); }) .then(() => { // download float32 return download(xhr, urlF32, 'arraybuffer'); }) .then((ab) => { // make sure download is correct const f32 = new Float32Array(ab); log('download urlF32 arraybuf', f32.byteLength, type(ab)); if (f32.byteLength !== F32.length) throw new Error(`Download from urlF32 has incorrect length: ${f32.byteLength} !== ${F32.length}`); }) .then(() => { return upload(xhr, urlUtf8, F32Utf8); }) .then((r) => { log('upload urlUtf8, F32Utf8 ', r); }) .then(() => { return download(xhr, urlF32, 'arraybuffer'); }) .then((ab) => { const f32 = new Float32Array(ab); log('download urlF32 arraybuf', f32.byteLength, type(ab)); if (f32.byteLength !== F32.length) throw new Error(`Download from urlF32 has incorrect length: ${f32.byteLength} !== ${F32.length}`); }) .then(() => { return upload(xhr, urlF32_2, F32_2); }) .then((r) => { log('upload urlF32_2, F32_2 ', r); }) .then(() => { return download(xhr, urlF32, 'arraybuffer'); }) .then((ab) => { const f32 = new Float32Array(ab) log('download urlF32 arraybuf', f32.byteLength, type(ab)); if (f32.byteLength !== F32.length) throw new Error(`Download from urlF32 has incorrect length: ${f32.byteLength} !== ${F32.length}`); }) .then(() => { log('XXXXXXXXXXXXXXXXX', urlUtf8_2, F32Utf8_2) return upload(xhr, urlUtf8_2, F32Utf8_2); }) .then((r) => { log('upload urlUtf8_2, F32Utf8_2', r); }) .then(() => { return download(xhr, urlUtf8_2, 'text'); }) .then((text2) => { const text2_f32 = stringToFloat32Array(text2); log('download urlUtf8_2 default', text2.length, type(text2), text2_f32); if (!isEqual(text2_f32, _f32_2)) throw new Error(`Download from urlUtf8_2 has incorrect content: ${text2_f32} !== ${_f32_2}`); }) .then(() => { return upload(xhr, urlJson, JSON.stringify(storageLength())); }) .then((r) => { log('upload:urlJson, storage ', r); }) .then(() => { return download(xhr, urlJson, 'json'); }) .then((json) => { log(`download urlJson json ${JSON.stringify(json).length}`, type(json), json); const testJson = storageLength(); if (JSON.stringify(json) !== JSON.stringify(testJson)) throw new Error(`Download from urlJson has incorrect content:\n ${JSON.stringify(json)} !== ${JSON.stringify(testJson)}`); }); } /** * Run the test. * If runTest() fails, an exception will be thrown. */ setTimeout(function () { runTest() .then(() => { console.log("PASSED"); shutdown(); }) .catch((e) => { console.log("FAILED", e); shutdown(); throw e; }); }, 100); function shutdown() { if (serverProcess) serverProcess.close(); serverProcess = null; } node-XMLHttpRequest-master/tests/test-sync-response.js000066400000000000000000000074261472526051200234450ustar00rootroot00000000000000/** * Test GET http URL with both async and sync mode. * Use xhr.responseType = "" and "arraybuffer". */ 'use strict'; var assert = require("assert") , spawn = require('child_process').spawn , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , serverProcess; const supressConsoleOutput = true; function log (_) { if ( !supressConsoleOutput) console.log.apply(console, arguments); } // Running a sync XHR and a webserver within the same process will cause a deadlock serverProcess = spawn(process.argv[0], [__dirname + "/server.js"], { stdio: 'inherit' }); setTimeout(function () { try { runTest(); console.log('PASSED'); } catch (e) { console.log('FAILED'); throw e; } finally { serverProcess.kill('SIGINT'); } }, 100); /** * Assumes hexStr is the in-memory representation of a Float32Array. * Relies on the fact that the char codes in hexStr are all <= 0xFF. * Returns Float32Array corresponding to hexStr. * * @param {string} hexStr * @returns {Float32Array} */ function stringToFloat32Array (hexStr) { const u8 = new Uint8Array(hexStr.length); for (let k = 0; k < hexStr.length; k++) u8[k] = Number(hexStr.charCodeAt(k)); return new Float32Array(u8.buffer); } /** * Check to see if 2 array-like objects have the same elements. * @param {{ length: number }} ar1 * @param {{ length: number }} ar2 * @returns {boolean} */ function isEqual (ar1, ar2) { if (ar1.length !== ar2.length) return false; for (let k = 0; k < ar1.length; k++) if (ar1[k] !== ar2[k]) return false; return true; } function runTest() { var xhr = new XMLHttpRequest(); var isSync = false; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // xhr.responseText is a 'utf8' string. var str = xhr.responseText; log('/text', str); assert.equal(xhr.responseText, "Hello world!"); assert.equal(xhr.getResponseHeader('content-type'), 'text/plain') isSync = true; } } xhr.open("GET", "http://localhost:8888/text", false); xhr.send(); assert(isSync, "XMLHttpRequest was not synchronous"); xhr = new XMLHttpRequest(); isSync = false; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // xhr.response is an ArrayBuffer var str = Buffer.from(xhr.response).toString('utf8'); log('/binary1', str); assert.equal(str, 'Hello world!'); assert.equal(xhr.getResponseHeader('content-type'), 'application/octet-stream') isSync = true; } } xhr.open("GET", "http://localhost:8888/binary1", false); xhr.responseType = 'arraybuffer'; xhr.send(); assert(isSync, "XMLHttpRequest was not synchronous"); xhr = new XMLHttpRequest(); isSync = false; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // xhr.response is an ArrayBuffer var binaryStr = Buffer.from(xhr.response).toString('binary'); var f32 = stringToFloat32Array(binaryStr); log('/binary2', f32); var answer = new Float32Array([1, 5, 6, 7]); assert.equal(isEqual(f32, answer), true); assert.equal(xhr.getResponseHeader('content-type'), 'application/octet-stream') isSync = true; } } xhr.open("GET", "http://localhost:8888/binary2", false); xhr.responseType = 'arraybuffer'; xhr.send(); assert(isSync, "XMLHttpRequest was not synchronous"); xhr = new XMLHttpRequest(); isSync = false; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { assert.equal(xhr.response.toString(), 'Hello world!'); assert.equal(xhr.getResponseHeader('content-type'), 'application/octet-stream') isSync = true; } } xhr.open("GET", "http://localhost:8888/binary1", false); xhr.send(); assert(isSync, "XMLHttpRequest was not synchronous"); console.log("done"); } node-XMLHttpRequest-master/tests/test-unsafe-redirect.js000066400000000000000000000017621472526051200237120ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , xhr = new XMLHttpRequest() , http = require("http"); // Test server var server = http.createServer(function (req, res) { if (req.url === '/redirectingResource') { res.writeHead(301, {'Location': 'file:///etc/passwd'}); res.end(); } else { var body = "Hello World"; res.writeHead(200, { "Content-Type": "text/plain", "Content-Length": Buffer.byteLength(body), "Date": "Thu, 30 Aug 2012 18:17:53 GMT", "Connection": "close" }); res.write("Hello World"); res.end(); } this.close(); }).listen(8000); xhr.onreadystatechange = function() { if (this.readyState === 4) { assert.equal(xhr.statusText, "Unsafe redirect"); assert.equal(xhr.status, 0); console.log("done"); } }; try { xhr.open("GET", "http://localhost:8000/redirectingResource"); xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } node-XMLHttpRequest-master/tests/test-url-origin.js000066400000000000000000000024501472526051200227140ustar00rootroot00000000000000var assert = require("assert") , XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest , spawn = require('child_process').spawn; // Test server var serverProcess = spawn(process.argv[0], [__dirname + "/server.js"], { stdio: 'inherit' }); var runTest = function () { try { let xhr = new XMLHttpRequest({ origin: "http://localhost:8888" }); xhr.open("GET", "text", false); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { assert.equal(xhr.getResponseHeader('Content-Type'), 'text/plain'); assert.equal(xhr.responseText, "Hello world!"); console.log("origin test 1: done"); } }; xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); } try { let xhr = new XMLHttpRequest({ origin: "http://localhost:8888/text" }); xhr.open("GET", "", false); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { assert.equal(xhr.getResponseHeader('Content-Type'), 'text/plain'); assert.equal(xhr.responseText, "Hello world!"); console.log("origin test 2: done"); } }; xhr.send(); } catch(e) { console.log("ERROR: Exception raised", e); throw e; } } setTimeout(function () { try { runTest(); } finally { serverProcess.kill('SIGINT'); } }, 100); node-XMLHttpRequest-master/tests/test-utf8-tearing.js000066400000000000000000000177401472526051200231520ustar00rootroot00000000000000 /****************************************************************************************** * Assume a web server serves up the utf8 encoding of a random Uint8Array, * so that xhr.responseText is a string corresponding to the in-memory * representation of the Uint8Array. This test demonstrates a bug in xmlhttprequest-ssl, * where the utf8 endcoding of a byte with 0x80 <= byte <= 0xff, is torn across 2 chunks. * * Consider a code point 0x80. The utf8 encoding has 2 bytes 0xc2 and 0x80. * It is possible for one chunk to end with 0xc2 and the next chunk starts with 0x80. * This is what is meant by tearing. The fix is to remove * self.responseText += data.toString('utf8'); * from the response 'data' handler and add the following to the response 'end' handler * // Construct responseText from response * self.responseText = self.response.toString('utf8'); */ 'use strict'; var assert = require("assert"); var http = require("http"); var XMLHttpRequest = require("../lib/XMLHttpRequest").XMLHttpRequest; var supressConsoleOutput = true; function log (_) { if ( !supressConsoleOutput) console.log(arguments); } var serverProcess; /****************************************************************************************** * This section produces a web server that serves up * 1) Buffer.from(ta.buffer) using url = "http://localhost:8888/binary"; * 2) utf8 encoding of ta_to_hexStr(ta) using url = "http://localhost:8888/binaryUtf8"; * where ta is a Float32Array. * Note: In order to repro utf8 tearing ta.length needs to be pretty big * N = 1 * 1000 * 1000; */ /** * Create a string corresponding to the in-memory representation of Float32Array ta. * * @param {Float32Array} ta * @returns {string} */ function ta_to_hexStr(ta) { var u8 = new Uint8Array(ta.buffer); return u8.reduce(function (acc, cur) { return acc + String.fromCharCode(cur) }, ""); } /** * Create a random Float32Array of length N. * * @param {number} N * @returns {Float32Array} */ function createFloat32Array(N) { assert(N > 0); var ta = new Float32Array(N); for (var k = 0; k < ta.length; k++) ta[k] = Math.random(); //ta = new Float32Array([1, 5, 6, 7]); // Use to debug return ta; } var N = 1 * 1000 * 1000; // Needs to be big enough to tear a few utf8 sequences. var f32 = createFloat32Array(N); /** * From a Float32Array f32 transform into: * 1) buffer: Buffer.from(ta.buffer) * 2) bufferUtf8: utf8 encoding of ta_to_hexStr(ta) * * @param {Float32Array} f32 * @returns {{ buffer: Buffer, bufferUtf8: Buffer }} */ function createBuffers(f32) { var buffer = Buffer.from(f32.buffer); var ss = ta_to_hexStr(f32); var bufferUtf8 = Buffer.from(ss, 'utf8'); // Encode ss in utf8 return { buffer, bufferUtf8 }; } var bufs = createBuffers(f32); var buffer = bufs.buffer, bufferUtf8 = bufs.bufferUtf8 /** * Serves up buffer at * url = "http://localhost:8888/binary"; * Serves up bufferUtf8 at * url = "http://localhost:8888/binaryUtf8"; * * @param {Buffer} buffer * @param {Buffer} bufferUtf8 */ function createServer(buffer, bufferUtf8) { serverProcess = http.createServer(function (req, res) { switch (req.url) { case "/binary": res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(buffer); return; case "/binaryUtf8": res.writeHead(200, {"Content-Type": "application/octet-stream"}) res.end(bufferUtf8); return; default: res.writeHead(404, {"Content-Type": "text/plain"}) res.end("Not found"); return; } }).listen(8888); process.on("SIGINT", function () { if (serverProcess) serverProcess.close(); serverProcess = null; }); } createServer(buffer, bufferUtf8); /****************************************************************************************** * This section tests the above web server and verifies the correct Float32Array can be * successfully reconstituted for both * 1) url = "http://localhost:8888/binary"; * 2) url = "http://localhost:8888/binaryUtf8"; */ /** * Assumes hexStr is the in-memory representation of a Float32Array. * Relies on the fact that the char codes in hexStr are all <= 0xFF. * Returns Float32Array corresponding to hexStr. * * @param {string} hexStr * @returns {Float32Array} */ function hexStr_to_ta(hexStr) { var u8 = new Uint8Array(hexStr.length); for (var k = 0; k < hexStr.length; k++) u8[k] = Number(hexStr.charCodeAt(k)); return new Float32Array(u8.buffer); } /** * Verify ta1 and ta2 are the same kind of view. * Verify the first count elements of ta1 and ta2 are equal. * * @param {Float32Array} ta1 * @param {Float32Array} ta2 * @param {number} [count=1000] * @returns {boolean} */ function checkEnough(ta1, ta2, count) { if (count === undefined) count = 1000 assert(ta1 && ta2); if (ta1.constructor.name !== ta2.constructor.name) return false; if (ta1.length !== ta2.length) return false; if (ta1.byteOffset !== ta2.byteOffset) return false; for (var k = 0; k < Math.min(count, ta1.length); k++) { if (ta1[k] !== ta2[k]) { log('checkEnough: Not Equal!', k, ta1[k], ta2[k]); return false; } } return true; } var xhr = new XMLHttpRequest(); var url = "http://localhost:8888/binary"; var urlUtf8 = "http://localhost:8888/binaryUtf8"; function download (xhr, url, responseType) { if (responseType === undefined) responseType = 'arraybuffer'; return new Promise(function (resolve, reject) { xhr.open("GET", url, true); xhr.responseType = responseType; xhr.onloadend = function () { if (xhr.status >= 200 && xhr.status < 300) { switch (responseType) { case "": case "text": resolve(xhr.responseText); break; case "document": resolve(xhr.responseXML); break; default: resolve(xhr.response); break; } } else { var errorTxt = `${xhr.status}: ${xhr.statusText}`; reject(errorTxt); } }; xhr.send(); }); } /** * Send a GET request to the server. * When isUtf8 is true, assume that xhr.response is already * utf8 encoded so that xhr.responseText. * * @param {string} url * @param {boolean} isUtf8 * @returns {Promise} */ function Get(url, isUtf8) { return download(xhr, url, 'text').then((dataTxt) => { return download(xhr, url, 'arraybuffer').then((ab) => { var data = Buffer.from(ab); assert(dataTxt && data); log('XHR GET:', dataTxt.length, data.length, data.toString('utf8').length); log('XHR GET:', data.constructor.name, dataTxt.constructor.name); if (isUtf8 && dataTxt.length !== data.toString('utf8').length) throw new Error("xhr.responseText !== xhr.response.toString('utf8')"); var ta = isUtf8 ? new Float32Array(hexStr_to_ta(dataTxt)) : new Float32Array(data.buffer); log('XHR GET:', ta.constructor.name, ta.length, ta[0], ta[1]); if (!checkEnough(ta, f32)) throw new Error("Unable to correctly reconstitute Float32Array"); return ta; }) }); } /** * Test function which gets utf8 encoded bytes of the typed array * new Uint8Array(new Float32Array(N).buffer), * then it gets the raw bytes from * new Uint8Array(new Float32Array(N).buffer). * Before the utf8 tearing bug is fixed, * Get(urlUtf8, true) * will fail with the exception: * Error: xhr.responseText !== xhr.response.toString('utf8'). * * @returns {Promise} */ function runTest() { return Get(urlUtf8, true) .then(function () { return Get(url, false); }); } /** * Run the test. */ setTimeout(function () { runTest() .then(function (ta) { console.log("done", ta && ta.length); if (serverProcess) serverProcess.close(); serverProcess = null; }) .catch(function (e) { console.log("FAILED"); if (serverProcess) serverProcess.close(); serverProcess = null; throw e; }) }, 100); node-XMLHttpRequest-master/tests/testBinaryData000066400000000000000000000000201472526051200221420ustar00rootroot00000000000000€? @À@à@node-XMLHttpRequest-master/tests/testdata.txt000066400000000000000000000000131472526051200216550ustar00rootroot00000000000000Hello World