How to stream the body of a fetch() call into a websocket (either binary or text)?

25 Views Asked by At

I'm writing a small NodeJS / TypeScript websocket client application that connects to another server application via websocket. It's basically a proxy tunneling application that works as follows:

Visitor <- (http) -> Server App <- (websocket) -> Client App <- (HTTP) -> Apache Server

The Client app and the Apache server are on the same internal network. There could be multiple Apache server backends, so depending on the domain name, we will select the right Apache server address from an internal array (see code below).

Let's assume the visitor is trying to visit "example.com", which is hosted on Apache server address 192.168.0.123.

My issue is, how do I use the fetch() method to retrieve: (1) the response header and send that over the websocket, then, (2) retrieve the body (text or binary) of the response and also send that across the websocket.

Below is the code I have so far, and I marked where I'm starting to get lost.

// Firstly, this is the header sent to the client app from the server app
{
  "Method": "GET",
  "URL": "http://example.com",
  "Header": {
    "Accept": [ "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
    ],
    "Accept-Encoding": [
      "gzip, deflate"
    ],
    "Accept-Language": [
      "en-US,en;q=0.5"
    ],
    "Cache-Control": [
      "no-cache"
    ],
    "Connection": [
      "keep-alive"
    ],
    "Host": [
      "example.com"
    ],
    "Pragma": [
      "no-cache"
    ],
    "Upgrade-Insecure-Requests": [
      "1"
    ],
    "User-Agent": [
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0"
    ]
  },
  "ContentLength": 0
}

And this is my client app code so far:

import { WebSocket } from "ws";

//--------------------------------------------------------

const ws = new WebSocket("ws://myserverapp:8080/ws", {
  headers: {
    Bearer: "xyz890"
  }
});

//--------------------------------------------------------

var hosts = [
  {
    host: "example.com",
    server: "192.168.0.123"
  },
  {
    host: "example.org",
    server: "192.168.0.238"
  }
];

//--------------------------------------------------------


ws.on('open', () => {

  console.log("Connected to server!");

});

//--------------------------------------------------------

ws.on('message', async function message(data, isBinary) {

  const message = isBinary ? data : data.toString();

  console.log("---------------------------------------------------");
  console.log("Received Message:");
  console.log(message);

  if (!isBinary) {

    if (isJSON(m)) {

      let parsedJson = JSON.parse(m);

      let url = new URL(parsedJson.URL);

      let server: string = getServerByHost(url.host);

      // *** START HERE ***

      let options = {
        method: parsedJson.Method,
        headers: parsedJson.Header
      };

      fetch(url.protocol + "//" + server + url.pathname + url.searchParams, options)
        .then(async response => {

          // The expected return to the server is the following json object
          let r = {
            statuscode: response.status,
            parsedJson: {},
            contentlength: Number(response.headers.get('content-length'))
          };

          response.headers.forEach((value, key) => {

            r.parsedJson[key] = [];

            r.parsedJson[key][0] = value;

          });

          // Send the response header back to the server first before the body
          ws.send(JSON.stringify(r));

          // *** NOW SEND THE BODY ***

          // *** COMPLETELY UNSURE WHAT I'M DOING HERE BELOW THIS LINE ***
          
          return response.body;

        })
        .then(async body => {

          const reader = body?.getReader();

          reader?.read().then(({done, value}) => {
            
            if(done) {

              return;

            }


            value.forEach((value, index, array) => {

             // *** WHAT HAPPENS HERE?? ***
             // *** IS THIS EVEN CORRECT?? ***

            });      

          });          

        });

    } else {

      // Not a JSON

    }

  } else {

    // Binary data received from websocket
    // What to do here??

  }

});


//--------------------------------------------------------

function getServerByHost(host: string): string {

  let result = hosts.find(targetHost => targetHost.host === host);

  if (result) {

    console.log("Found host: ", result);

    return result.server;

  }

  return "";

}

//--------------------------------------------------------

// Function to test string is a JSON
function isJSON(str) {

  try {

    JSON.stringify(JSON.parse(str));

    return true;

  } catch (e) {

    return false;

  }

}

//--------------------------------------------------------

0

There are 0 best solutions below