browser to localhost

Here is a way to send URLs from the webbrowser to my local (shell)scripts.

among many other things, this allows me to:


This userscript can be used with browser extensions like f.i. Violentmonkey

It listens for key events, if the event is the user pressing Alt+p, it will send a HTTP Request to http://127.0.0.1 (localhost) port 8210. With a JSON object containing the single key "url": that holds the URL currently open in the browser.

~/dotfiles/web/userscripts/url-to-localhost.user.js
// ==UserScript==
// @name        Send current URL to localhost
// @namespace   nergen.net
// @include     *
// @run-at      document-start
// @grant       GM.xmlHttpRequest
// @version     1.0
// @author      nergen
// @description 2023/7/8, 5:47:20 PM
// ==/UserScript==

document.addEventListener('keydown', function(e) {

  const host = "http://127.0.0.1";
  const port = 8210;

  // Alt+p to trigger
  if (e.code == "KeyP" && e.altKey) {

    GM.xmlHttpRequest({
      method: "POST",
      url: `${host}:${port}`,
      headers: {"Origin": "userscript"},
      data: JSON.stringify({
        url: location.href,
      })
    });
  }
  return false;
});

Without a local webserver the userscript is useless. To see that it works we can start NetCat listening to port 8210, by executing the command nc -l -p 8210 in a terminal:

002 ~ > nc -l -p 8210
POST / HTTP/1.1
Host: 127.0.0.1:8210
Connection: keep-alive
Content-Length: 78
sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114"
Content-Type: text/plain;charset=UTF-8
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
sec-ch-ua-platform: "Linux"
Accept: */*
Sec-Fetch-Site: none
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9

{"url":"https://github.com/avleen/bashttpd/blob/master/bashttpd"}

Personally I use a local python webserver:

~/dotfiles/web/userscripts/userscript-server.py
#!/usr/bin/python3

from json import loads
import subprocess
from http.server import HTTPServer, BaseHTTPRequestHandler

SERVER_PORT = 8210
SERVER_HOST = '127.0.0.1'
COMMAND = 'notify-send'

class UserScriptServer(BaseHTTPRequestHandler):

    def do_POST(self):

        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.end_headers()
        origin = self.headers.get('Origin', '')

        if origin == "userscript":

            length = int(self.headers['Content-Length'])
            content_str = self.rfile.read(length).decode('utf8')
            content = loads(content_str)

            print("userscript request:\n%s\n\n"%(content['url']))

            if len(content['cmd']) > 0:
                subprocess.Popen([COMMAND, content['url']])
                self.wfile.write(''.encode('utf8'))

        else:
            print("WARNING, UNKNOWN request to userscript-server!! \n")


def init_server(server_class=HTTPServer):
    server_address = (SERVER_HOST, SERVER_PORT)
    httpd = server_class(server_address, UserScriptServer)
    httpd.serve_forever()


try:
    init_server()
except KeyboardInterrupt:
    pass

This will pass the URL as an argument to the COMMAND, in the example above notify-send but you could replace this with your own URL handler.

This was a demonstration of an example setup, obviously the webserver and URL-handler can be done in many other ways and languages. The userscript itself could be extended, to pass more stuff in the "data": JSON, or maybe pre-format it depending on the context, e.t.c.