Skip to content
/ fakettp Public

sandbox node core http module in a service worker.

License

Notifications You must be signed in to change notification settings

3p3r/fakettp

Repository files navigation

fakettp

sandbox node core http module in a service worker.

disclaimer

this is part of a security focused research project I am currently working on. as it usually goes for security software, this can be used for good or evil. this can also harm your computer or browser and cause you inconvenience. I do not take any responsibility for any damages caused by this software. proceed at your own risk. absolutely no warranty is provided. you may brick your browser if you do not know how to use this library properly.

if you are new, it is not all hopeless. make sure you understand the intricacies of service workers and its security implications before using this library.

motivation

in building progressive web apps and performing client side testing and caching, it is immensely helpful to have something like express or socket.io available in order to reuse packages on npm made for various http interaction needs.

using this library in a bundler allows reuse of applications made for node.js in terms of interaction with the core http module.

creating the http server this way does not involve a network adapter and is thus accessible where environment is restricted. sending requests in browsers is also local and does not involve any network adapter (including loopback).

usage

this library is designed to be used in a bundler like webpack. fakettp is a drop in replacement for node's http module. two requirements should be met to use it in a bundler successfully:

  1. alias http to fakettp in your bundler config.
  2. provide fallbacks for anything else that your code may use from node core.

example webpack config:

npm install fakettp --save-dev
module.exports = {
  resolve: {
    plugins: [
      new webpack.ProvidePlugin({
        process: "process/browser",
        Buffer: ["buffer", "Buffer"],
        URL: ["url", "URL"],
      }),
    ],
    fallback: {
      http: require.resolve("fakettp"),
      util: require.resolve("util/"),
      events: require.resolve("events/"),
      buffer: require.resolve("buffer/"),
      assert: require.resolve("assert/"),
      stream: require.resolve("stream-browserify"),
    }
  },
};

you can use configuration of this repository as reference.

demo

check out the examples that are known to work in samples folder and the webpack config for this repository.

demo

you can create a server and listen like so:

const server = FAKETTP.createServer().on("request", (req, res) => {
  req.on('data', (chunk) => {
    console.log(`Received ${chunk.length} bytes of data in request body.`);
    console.log(`Request chunk: ${chunk.toString()}`);
  });
  req.on('end', () => {
    console.log('No more data in request body.');
  });
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('{"yo":"nice"}');
}).listen(443, "google.com");

listen

after that all requests will be intercepted and handled by the service worker. the host and port to watch for can be configured by passing options to listen.

you can for example send a request to the server like so:

async function postData(data = {}, url = "https://google.com/fake.html") {
  const response = await fetch(url, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify(data),
  });
  return response.json();
}

postData({ answer: "browser" }).then((data) => {
  console.log(data);
});

response

you can also close the server like so:

server.close();

and if you attempt to send a request after that, you will get an error:

close

development

  • npm test to run tests.
  • npm run build to build the project.
  • npm run serve to start webpack dev server.
  • npm run watch to watch for changes and rebuild.
  • npx http-serve --cors dist to run production build.

compatibility

working samples:

  • ext/samples/express.ts: express app with a dynamic route.
  • ext/samples/express-post.ts: express app with a post route.
  • ext/samples/express-static.ts: express app with static files.
  • ext/samples/socket-io.ts: express app with socket io.
  • ext/samples/remote-context: shows the usage of fakettp.html.

this is what is known to work good enough for most use cases:

  • http.createServer([options][, requestListener]) one instance per session
  • http.METHODS through stream-http
  • http.STATUS_CODES through stream-http
  • http.get(options[, callback]) through stream-http
  • http.get(url[, options][, callback]) through stream-http
  • http.request(options[, callback]) through stream-http
  • http.request(url[, options][, callback]) through stream-http
  • class: http.ClientRequest through stream-http
  • class: http.Server
    • event: 'close'
    • event: 'connection'
    • event: 'request'
    • server.close([callback])
    • server.listen()
    • server.listen(port[, host][, callback])
    • server.listening
  • class: http.ServerResponse
    • event: 'close'
    • event: 'finish'
    • response.end([data][, encoding][, callback])
    • response.finished
    • response.getHeader(name)
    • response.getHeaderNames()
    • response.getHeaders()
    • response.hasHeader(name)
    • response.headersSent
    • response.removeHeader(name)
    • response.setHeader(name, value)
    • response.socket
    • response.statusCode
    • response.statusMessage
    • response.write(chunk[, encoding][, callback])
    • response.writeHead(statusCode[, statusMessage][, headers])
  • class: http.IncomingMessage
    • event: 'close'
    • message.complete
    • message.destroy([error])
    • message.headers
    • message.httpVersion
    • message.method
    • message.rawHeaders
    • message.socket
    • message.statusCode
    • message.statusMessage
    • message.url
    • message.connection
  • class: net.Socket
    • event: 'close'
    • event: 'data'
    • event: 'drain'
    • event: 'end'
    • event: 'error'
    • socket.address()
    • socket.remoteAddress
    • socket.remoteFamily
    • socket.remotePort
    • socket.bufferSize
    • socket.bytesRead
    • socket.bytesWritten
    • socket.destroy([exception])
    • socket.destroyed
    • socket.end([data][, encoding][, callback])
    • socket.write(data[, encoding][, callback])

limitations

  • fakettp cannot be hosted on a cdn and must be served from the same origin.
  • fakettp works best in chromium based browsers due to support discrepancies.
  • fakettp only works in secure contexts and localhost for development purposes.
  • fakettp does not support history navigations that result in crossing origins.
  • fakettp listening on root domain is not advised due to history complications.
  • fakettp can currently have only one instance active and listening at a time.