This is the fifth day of my participation in Gwen Challenge

When native front-end code tries to interconnect with the back-end interface, we often use tools like Webpack Dev Server to configure the proxy:

// webpack.config.js
module.exports = {
  / /...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000',}}};Copy the code

You have to reboot each configuration change to take effect. In fact, there is no such trouble, with Chrome debugging, just use Chrome provides the proxy interface to easily configure the change proxy immediately!

There are dozens of Chrome plugins on the market, such as Proxy SwitchyOmega:

You can make one yourself. That’s easy. We just need to:

  • manifest.json
  • Listening triggers the proxybackground.js
  • A configuration page

There are two interfaces that can implement proxy effects in the V2 extension:

  1. Proxy interface
  2. WebRequest interface

Each has its own strengths and weaknesses. Let’s see how each method works:

Using the Proxy Interface

The proxy interface is less flexible than webRequest and can only specify the destination server address (domain name or IP address) of the proxy. But its performance is better.

The manifest. Json configuration

{
  "name": "PowerProxy"."description": ""."manifest_version": 2."version": "1.0.0"."permissions": [
    "storage"."proxy"."background"."http://localhost/*"]."icons": {
    "32": "32.png"."64": "64.png"."128": "128.png"
  },
  "browser_action": {
    "default_icon": "32.png"."default_title": "PowerProxy"
  },
  "background": {
    "scripts": ["background.js"]."persistent": true}}Copy the code

In addition to basic information such as name description and version, the following permissions are declared:

  • storageStorage proxy configuration
  • proxyConfigure proxy
  • background— Handle proxy requests
  • http://localhost/*Domain name restriction* : / / * / *Match all domain names

Proxy configuration page

Here we use a crude, pure HTML example to create JSON for the form-filling proxy configuration:

<! -- index.html -->
<html>
  <head></head>
  <body>
    <form id="form" action="#" onsubmit="javascript:save()">
      <textarea name="rules" rows="30"></textarea>
      <p id="buttons">
        <input type="submit" value="Save" />
      </p>
    </form>
    <script type="text/javascript">
      var form = document.getElementById('form')
      var rulesField = document.getElementById('id')
      chrome.storage.local.get(['rules'].({ rules }) = > {
        form.rules.value = JSON.stringify(rules)
      })

      function save(e) {
        chrome.storage.local.set(
          {
            rules: form.rules.value,
          },
          () = > null
        )
        return false
      }
    </script>
  </body>
</html>
Copy the code

Here, we use Chrome’s local storage to store the configuration, and background.js can read the configuration from it to perform corresponding operations. Here I store it as a text type, or I can choose to store the JS object type directly.

The expected CONFIGURATION JSON format is as follows:

[{
  "url": "http://localhost:8000/api/a"."proxy": "http://www.example.com"
}]
Copy the code

And we match from top to bottom, that is, the first configuration has the highest priority.

Background.js listens to trigger the proxy

After creating the configuration page, we also need a portal to open the page. Here, I use the Browser Action of Chrome, that is, the small icon in the upper right corner of the address bar as the entrance, and listen for clicking events in background-js to open the page:

chrome.browserAction.onClicked.addListener(() = > {
  chrome.tabs.create({
    url: chrome.runtime.getURL('index.html'), 
    active: true 
 	}, 
	() = > null
});
Copy the code

Then adjust the browser proxy Settings based on our configuration JSON:

chrome.storage.onChanged.addListener((changes) = > {
  const { newValue: rules } = changes.rules;
  var config = {
    mode: "pac_script".pacScript: {
      data: `
const rules = ${rules};
function FindProxyForURL(url, host) {
	const rule = rules.find(rule => url.startsWith(rule.url));
	if (rule) {
		return 'PROXY ${rule.proxy}';
	}
	return 'DIRECT';
}
`}}; chrome.proxy.settings.set( {value: config, scope: 'regular' },
    function() {}); });Copy the code

Redirection using the webRequest interface

WebRequest has poor performance, but it is very flexible and easier to use.

The manifest. Json configuration

{
  "name": "PowerProxy"."description": ""."manifest_version": 2."version": "1.0.0"."permissions": [
    "storage"."webRequest"."webRequestBlocking"."background"."http://localhost/*"]."icons": {
    "32": "32.png"."64": "64.png"."128": "128.png"
  },
  "browser_action": {
    "default_icon": "32.png"."default_title": "PowerProxy"
  },
  "background": {
    "scripts": ["background.js"]."persistent": true}}Copy the code

In addition to basic information such as name description and version, the following permissions are declared:

  • storageStorage proxy configuration
  • webRequestandwebRequestBlockingConfigure proxy
  • background— Handle proxy requests
  • http://localhost/*Domain name restriction* : / / * / *Match all domain names

Proxy configuration page

This part is exactly the same as using the proxy interface.

Background.js listens to trigger the proxy

In the background. In addition to handling browserAction in js, we also need to monitor the chrome. WebRequest. OnBeforeRequest events:

let rules = [];
chrome.storage.local.get(['rules'].(data) = > {
  rules = JSON.parse(data.rules) || [];
});
chrome.storage.onChanged.addListener((changes) = > {
  const { newValue } = JSON.parse(changes.rules);
  rules = newValue;
});

chrome.webRequest.onBeforeRequest.addListener(
  ({ url }) = > {
    const rule = rules.find(rule= > url.startsWith(rule.url));
    if (rule) {
      return {
       redirectUrl: url.replace(rule.url, rule.proxy) }; }}, {urls: ['<all_urls>'].types: ['xmlhttprequest'],}, ['blocking'])Copy the code

Since this event is handled synchronously, but we read the storage asynchronously, we do an extra thing to read the configuration of the agent in a variable for subsequent operations.

summary

A simple Chrome extension is complete. If you like, help me point a [like] bar!

Previous recommendations:

  • Make a little Chrome extension mock request from this test like flying
  • How to debug the VSCode Debugger using NodeJS
  • Two paging apis that the front end must understand