How to modify a response in Electron
Asked Answered
A

4

7

Let's say that I'm using a GET request on https://example.com and the response is this:

This is a response message.

How would I modify it in a way so that in my code, so that it can change the response to say something like this:

This is a MODIFIED response message.

For example, if my Electron app were to navigate to https://example.com, the screen would show me the modified content instead of the original content.

Essentially, I am trying to literally modify the request.

I have based my code off of this question but it only shows a proof of concept with a pre-typed Buffer, as in my situation I'd like modify the response instead of outright replacing it. So, my code looks like this:

protocol.interceptBufferProtocol("http", (req, CALLBACK) => {
  if(req.url.includes("special url here")) {
  var request = net.request({
    method: req.method,
    headers: req.headers,
    url: req.url
  });
  request.on("response", (rp) => {
    var d = [];
    rp.on("data", c => d.push(c));
    rp.on("end", () => {
      var e = Buffer.concat(d);
      console.log(e.toString());
      // do SOMETHING with 'e', the response, then callback it.
      CALLBACK(e);
    });
  });
  request.end();
  } else {
    // Is supposedly going to carry out the request without interception
    protocol.uninterceptProtocol("http");
  }
}

This is supposed to manually request the URL, grab the response and return it. Without the protocol event, it works and gives me a response, but after some debugging, this piece of code consistently calls the same URL over and over with no response.

There is also the WebRequest API, but there is no way of modifying the response body, you can only modify the request headers & related content.

I haven't looked fully into Chromium-based solutions, but after looking at this, I'm not sure if it is possible to modify the response so it appears on my app's end in the first place. Additionally, I'm not familiar with the Chromium/Puppeteer messages that get sent all over the place.

Is there an elegant way to have Electron to get a URL response/request, call the URL using the headers/body/etc., then save & modify the response to appear different in Electron?

Anny answered 6/11, 2022 at 19:32 Comment(0)
P
2

I got it resolved by using the Electron protocol module to intercept and modify the response:

const { app, protocol } = require('electron');
const net = require('net');

app.on('ready', () => {
  protocol.interceptBufferProtocol('http', (req, callback) => {
    if (req.url.includes('special url here')) {
      const request = net.request({
        method: req.method,
        headers: req.headers,
        url: req.url
      });

      request.on('response', (response) => {
        const chunks = [];

        response.on('data', (chunk) => chunks.push(chunk));
        response.on('end', () => {
          let modifiedResponse = Buffer.concat(chunks);

          //for modifying response here
          const modifiedText = 'This is a MODIFIED response message.';
          modifiedResponse = Buffer.from(modifiedText, 'utf8');

          callback(modifiedResponse);
        });
      });

      request.end();
    } else {
      callback();
    }
  });
});
  1. Here, intercept the 'http' protocol using protocol.interceptBufferProtocol, check if the request URL contains the 'special-url', In that case, we make a new request using net.request with the same method, headers, and URL as the original request. and, listen for the response from the new request and concatenate the chunks of data
  2. Modify the response by replacing it with a new Buffer containing the modified text
  3. Call the callback with the modified response.
Pericycle answered 2/4 at 9:32 Comment(0)
G
1

I understand that your protocol interception handler recursively invokes itself because the net.request leads to another invocation of the same protocol. You could avoid this by inserting a special header that ends the recursion:

if (req.url.includes("special url here") &&
    !req.headers["x-request-interception"]) {
  var request = net.request({
    method: req.method,
    headers: {...req.headers, "x-request-interception": "true"},
    url: req.url
  });
  ...
} else {
    // Is supposedly going to carry out the request without interception
    protocol.uninterceptProtocol("http");
  }
}

Instead of adding a special header, you could also use a special value for the User-Agent header.

Am I right to assume that once the else branch has been executed, request interception is switched off completely? This would mean that each run of your electron program can intercept only one request in total. A less drastic else branch is given in Vinutha's answer.

Gradygrae answered 27/3 at 13:56 Comment(0)
S
1

This is similar to the previous answer but with some potentially more graceful handles:

const { app, protocol, net } = require('electron');
const originalProtocol = protocol.registerStandardSchemes || protocol.registerSchemesAsPrivileged;

app.on('ready', () => {
  originalProtocol(['http'], { secure: true });

  protocol.interceptBufferProtocol('http', (req, callback) => {
    if(req.url.includes("example.com")) {
      let request = net.request({
        method: req.method,
        headers: req.headers,
        url: req.url
      });

      request.on('response', (response) => {
        let data = [];
        response.on('data', (chunk) => data.push(chunk));
        response.on('end', () => {
          let buffer = Buffer.concat(data);
          // Convert buffer to string to modify it
          let bodyString = buffer.toString();
          // Modify the response
          let modifiedBodyString = bodyString.replace('This is a response message.', 'This is a modified response message.');
          // Send it back to buffer
          let modifiedBuffer = Buffer.from(modifiedBodyString);
          // Pass the modified buffer to callback 
          callback({ data: modifiedBuffer, mimeType: 'text/html' }); // Adjust mimeType accordingly
        });
      });

      request.end();
    } else {
      // For other URLs, proceed without modification
      callback();
    }
  }, (error) => {
    if (error) console.error('Failed.');
  });
});
Sigismundo answered 3/4 at 12:53 Comment(0)
S
0

fyi, intercept*Protocol functions are now deprecated in Electron. Replacement is protocol.handle.

Also a custom header is not needed with the new function, neither unhandling protocol handler, and there won't be an infinite loop anymore, but you need to forward the request to the correct network stack. For eg. if you register the protocol for a certain session, the net.request should be called from that session

Sarmentum answered 3/4 at 22:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.