mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 17:13:21 +00:00 
			
		
		
		
	GUACAMOLE-896: Avoid XHR-related memory limitations by using Fetch API.
The Fetch API allows us to read HTTP responses as true streams, without building an in-memory string. We can the buffer things ourselves as we see fit, including as a Blob that can dynamically leverage disk storage for larger data.
This commit is contained in:
		| @@ -1346,13 +1346,14 @@ Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTu | ||||
|     var tunnel = this; | ||||
|  | ||||
|     /** | ||||
|      * The current, in-progress HTTP request. If no request is currently in | ||||
|      * progress, this will be null. | ||||
|      * AbortController instance which allows the current, in-progress HTTP | ||||
|      * request to be aborted. If no request is currently in progress, this will | ||||
|      * be null. | ||||
|      * | ||||
|      * @private | ||||
|      * @type {XMLHttpRequest} | ||||
|      * @type {AbortController} | ||||
|      */ | ||||
|     var xhr = null; | ||||
|     var abortController = null; | ||||
|  | ||||
|     /** | ||||
|      * Additional headers to be sent in tunnel requests. This dictionary can be | ||||
| @@ -1364,23 +1365,6 @@ Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTu | ||||
|      */ | ||||
|     var extraHeaders = extraTunnelHeaders || {}; | ||||
|  | ||||
|     /** | ||||
|      * Adds the configured additional headers to the given request. | ||||
|      * | ||||
|      * @param {!XMLHttpRequest} request | ||||
|      *     The request where the configured extra headers will be added. | ||||
|      * | ||||
|      * @param {!object} headers | ||||
|      *     The headers to be added to the request. | ||||
|      * | ||||
|      * @private | ||||
|      */ | ||||
|     function addExtraHeaders(request, headers) { | ||||
|         for (var name in headers) { | ||||
|             request.setRequestHeader(name, headers[name]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     this.sendMessage = function sendMessage(elements) { | ||||
|         // Do nothing | ||||
|     }; | ||||
| @@ -1393,18 +1377,10 @@ Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTu | ||||
|         // Connection is now starting | ||||
|         tunnel.setState(Guacamole.Tunnel.State.CONNECTING); | ||||
|  | ||||
|         // Start a new connection | ||||
|         xhr = new XMLHttpRequest(); | ||||
|         xhr.open('GET', url); | ||||
|         xhr.withCredentials = !!crossDomain; | ||||
|         addExtraHeaders(xhr, extraHeaders); | ||||
|         xhr.responseType = 'text'; | ||||
|         xhr.send(null); | ||||
|  | ||||
|         var offset = 0; | ||||
|  | ||||
|         // Create Guacamole protocol parser specifically for this connection | ||||
|         // Create Guacamole protocol and UTF-8 parsers specifically for this | ||||
|         // connection | ||||
|         var parser = new Guacamole.Parser(); | ||||
|         var utf8Parser = new Guacamole.UTF8Parser(); | ||||
|  | ||||
|         // Invoke tunnel's oninstruction handler for each parsed instruction | ||||
|         parser.oninstruction = function instructionReceived(opcode, args) { | ||||
| @@ -1412,51 +1388,62 @@ Guacamole.StaticHTTPTunnel = function StaticHTTPTunnel(url, crossDomain, extraTu | ||||
|                 tunnel.oninstruction(opcode, args); | ||||
|         }; | ||||
|  | ||||
|         // Continuously parse received data | ||||
|         xhr.onreadystatechange = function readyStateChanged() { | ||||
|         // Allow new request to be aborted | ||||
|         abortController = new AbortController(); | ||||
|  | ||||
|             // Parse while data is being received | ||||
|             if (xhr.readyState === 3 || xhr.readyState === 4) { | ||||
|         // Stream using the Fetch API | ||||
|         fetch(url, { | ||||
|             headers : extraHeaders, | ||||
|             credentials : crossDomain ? 'include' : 'same-origin', | ||||
|             signal : abortController.signal | ||||
|         }) | ||||
|         .then(function gotResponse(response) { | ||||
|  | ||||
|                 // Connection is open | ||||
|                 tunnel.setState(Guacamole.Tunnel.State.OPEN); | ||||
|             // Reset state and close upon error | ||||
|             if (!response.ok) { | ||||
|  | ||||
|                 var buffer = xhr.responseText; | ||||
|                 var length = buffer.length; | ||||
|                 if (tunnel.onerror) | ||||
|                     tunnel.onerror(new Guacamole.Status( | ||||
|                         Guacamole.Status.Code.fromHTTPCode(response.status), response.statusText)); | ||||
|  | ||||
|                 // Parse only the portion of data which is newly received | ||||
|                 if (offset < length) { | ||||
|                     parser.receive(buffer.substring(offset)); | ||||
|                     offset = length; | ||||
|                 } | ||||
|                 tunnel.disconnect(); | ||||
|                 return; | ||||
|  | ||||
|             } | ||||
|  | ||||
|             // Clean up and close when done | ||||
|             if (xhr.readyState === 4) | ||||
|                 tunnel.disconnect(); | ||||
|             // Connection is open | ||||
|             tunnel.setState(Guacamole.Tunnel.State.OPEN); | ||||
|  | ||||
|         }; | ||||
|             var reader = response.body.getReader(); | ||||
|             var processReceivedText = function processReceivedText(result) { | ||||
|  | ||||
|         // Reset state and close upon error | ||||
|         xhr.onerror = function httpError() { | ||||
|                 // Clean up and close when done | ||||
|                 if (result.done) { | ||||
|                     tunnel.disconnect(); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|             // Fail if file could not be downloaded via HTTP | ||||
|             if (tunnel.onerror) | ||||
|                 tunnel.onerror(new Guacamole.Status( | ||||
|                     Guacamole.Status.Code.fromHTTPCode(xhr.status), xhr.statusText)); | ||||
|                 // Parse only the portion of data which is newly received | ||||
|                 parser.receive(utf8Parser.decode(result.value)); | ||||
|  | ||||
|             tunnel.disconnect(); | ||||
|         }; | ||||
|                 // Continue parsing when next chunk is received | ||||
|                 reader.read().then(processReceivedText); | ||||
|  | ||||
|             }; | ||||
|  | ||||
|             // Schedule parse of first chunk | ||||
|             reader.read().then(processReceivedText); | ||||
|  | ||||
|         }); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     this.disconnect = function disconnect() { | ||||
|  | ||||
|         // Abort and dispose of XHR if a request is in progress | ||||
|         if (xhr) { | ||||
|             xhr.abort(); | ||||
|             xhr = null; | ||||
|         // Abort any in-progress request | ||||
|         if (abortController) { | ||||
|             abortController.abort(); | ||||
|             abortController = null; | ||||
|         } | ||||
|  | ||||
|         // Connection is now closed | ||||
|   | ||||
		Reference in New Issue
	
	Block a user