Web relay can now stream http content-length requests to the device, #4172

Ylian Saint-Hilaire committed Jun 27, 2022 at 13:18 UTC e351e839db1cdc9d6f790e6ad020eb547f6f4563
1 file changed +9 -81
apprelays.js
+9 -81
@@ -197,11 +197,17 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) {
197 if (parent.webCookie != null) { request += 'cookie: ' + parent.webCookie + '\r\n' } // If we have a sessin cookie, use it.
198 request += '\r\n';
199
200 - if ((req.headers['transfer-encoding'] != null) || (req.headers['content-length'] != null)) {
200 + if (req.headers['content-length'] != null) {
201 + // Stream the HTTP request and body, this is a content-length HTTP request, just forward the body dataf
202 + send(Buffer.from(request));
203 + req.on('data', function (data) { send(data); }); // TODO: Flow control (Not sure how to do this in ExpressJS)
204 + req.on('end', function () { });
205 + } else if (req.headers['transfer-encoding'] != null) {
206 // Read the HTTP body and send the request to the device
207 + console.log('chunk stream start');
208 obj.requestBinary = [Buffer.from(request)];
203 - req.on('data', function (data) { obj.requestBinary.push(data); });
204 - req.on('end', function () { send(Buffer.concat(obj.requestBinary)); delete obj.requestBinary; });
209 + req.on('data', function (data) { console.log('chunk stream data'); obj.requestBinary.push(data); });
210 + req.on('end', function () { console.log('chunk stream end');send(Buffer.concat(obj.requestBinary)); delete obj.requestBinary; });
211 } else {
212 // Request has no body, send it now
213 send(Buffer.from(request));
@@ -333,84 +339,6 @@ module.exports.CreateWebRelay = function (parent, db, args, domain) {
339 }
340 }
341
336 - /*
337 - // Process incoming HTTP data
338 - obj.socketAccumulator = '';
339 - obj.socketParseState = 0;
340 - function processHttpData(data) {
341 - obj.socketAccumulator += data;
342 - while (true) {
343 - //console.log('ACC(' + obj.socketAccumulator + '): ' + obj.socketAccumulator);
344 - if (obj.socketParseState == 0) {
345 - var headersize = obj.socketAccumulator.indexOf('\r\n\r\n');
346 - if (headersize < 0) return;
347 - //obj.Debug("Header: "+obj.socketAccumulator.substring(0, headersize)); // Display received HTTP header
348 - obj.socketHeader = obj.socketAccumulator.substring(0, headersize).split('\r\n');
349 - obj.socketAccumulator = obj.socketAccumulator.substring(headersize + 4);
350 - obj.socketParseState = 1;
351 - obj.socketData = '';
352 - obj.socketXHeader = { Directive: obj.socketHeader[0].split(' ') };
353 - for (var i in obj.socketHeader) {
354 - if (i != 0) {
355 - var x2 = obj.socketHeader[i].indexOf(':');
356 - obj.socketXHeader[obj.socketHeader[i].substring(0, x2).toLowerCase()] = obj.socketHeader[i].substring(x2 + 2);
357 - }
358 - }
359 - }
360 - if (obj.socketParseState == 1) {
361 - var csize = -1;
362 - if ((obj.socketXHeader['connection'] != null) && (obj.socketXHeader['connection'].toLowerCase() == 'close') && ((obj.socketXHeader["transfer-encoding"] == null) || (obj.socketXHeader["transfer-encoding"].toLowerCase() != 'chunked'))) {
363 - // The body ends with a close, in this case, we will only process the header
364 - csize = 0;
365 - } else if (obj.socketXHeader['content-length'] != null) {
366 - // The body length is specified by the content-length
367 - csize = parseInt(obj.socketXHeader['content-length']);
368 - if (obj.socketAccumulator.length < csize) return;
369 - var data = obj.socketAccumulator.substring(0, csize);
370 - obj.socketAccumulator = obj.socketAccumulator.substring(csize);
371 - obj.socketData = data;
372 - csize = 0;
373 - } else {
374 - // The body is chunked
375 - var clen = obj.socketAccumulator.indexOf('\r\n');
376 - if (clen < 0) return; // Chunk length not found, exit now and get more data.
377 - // Chunk length if found, lets see if we can get the data.
378 - csize = parseInt(obj.socketAccumulator.substring(0, clen), 16);
379 - if (obj.socketAccumulator.length < clen + 2 + csize + 2) return;
380 - // We got a chunk with all of the data, handle the chunck now.
381 - var data = obj.socketAccumulator.substring(clen + 2, clen + 2 + csize);
382 - obj.socketAccumulator = obj.socketAccumulator.substring(clen + 2 + csize + 2);
383 - try { obj.socketData += data; } catch (ex) { console.log(ex, typeof data, data.length); }
384 - }
385 - if (csize == 0) {
386 - //obj.Debug("xxOnSocketData DONE: (" + obj.socketData.length + "): " + obj.socketData);
387 - processHttpResponse(obj.socketXHeader, obj.socketData);
388 - obj.socketParseState = 0;
389 - obj.socketHeader = null;
390 - }
391 - }
392 - }
393 - }
394 -
395 - // This is a fully parsed HTTP response from the remote device
396 - function processHttpResponse(header, data) {
397 - //console.log('processHttpResponse', header);
398 - obj.res.status(parseInt(header.Directive[1])); // Set the status
399 - const blockHeaders = ['Directive' ]; // These are headers we do not forward
400 - for (var i in header) {
401 - if (i == 'set-cookie') { parent.webCookie = header[i]; } // Keep the cookie, don't forward it
402 - else if (blockHeaders.indexOf(i) == -1) { obj.res.set(i, header[i]); } // Set the headers if not blocked
403 - }
404 - obj.res.set('Content-Security-Policy', "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;"); // Set an "allow all" policy, see if the can restrict this in the future
405 - obj.res.end(data, 'binary'); // Write the data
406 - delete obj.res;
407 - parent.lastOperation = obj.lastOperation = Date.now(); // Update time of last opertion performed
408 -
409 - // Event completion
410 - if (obj.oncompleted) { obj.oncompleted(obj.tunnelId); }
411 - }
412 - */
413 -
342 // Process incoming HTTP data
343 obj.socketAccumulator = '';
344 obj.socketParseState = 0;