rwf2 / Rocket

A web framework for Rust.

Home Page:https://rocket.rs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Inject script to auto-reload browser when Rocket restarts

rikhuijzer opened this issue · comments

What's missing?

First of all, thanks for making Rocket. I like it a lot.

I would like to open a feature request. Whenever I change my templates or Rocket code, Rocket restarts automatically with

cargo watch -x run

as described in #292 (comment). However, my browser doesn't automatically refresh the page meaning that I manually have to refresh it upon each change.

Ideal Solution

The ideal solution would be something like what is used in the npm live-server package. There, some Javascript is injected which connects to an open socket. Whenever the server reloads some files, the browser receives a notification and reloads the page.

Why can't this be implemented outside of Rocket?

I wouldn't know how to implement this outside of Rocket.

Are there workarounds usable today?

Yes. One workaround that works reasonably well for me is to inject the following Javascript into my HTML pages when in development mode:

<script>
  function check_reload() {
      fetch("/check_reload").then(response => response.text()).then(data => {
          if (data === "true") {
              location.reload();
          }
      });
  }
  setInterval(check_reload, 2000);
</script>

and to add the following endpoint to my Rocket server:

lazy_static! {
    static ref SHOULD_RELOAD: Mutex<bool> = Mutex::new(true);
}

#[get("/check_reload")]
async fn check_reload() -> String {
    let mut should_reload = SHOULD_RELOAD.lock().unwrap();
    if *should_reload {
        *should_reload = false;
        return "true".to_string();
    } else {
        return "false".to_string();
    }
}

So, what happens here is that the browser asks Rocket ever 2 seconds whether the browser should reload. If Rocket just reloaded, the answer is true. If Rocket has been asked this before but didn't restart, then the answer is false.

There are some drawbacks to this approach though:

  1. I have to set log_level = "critical" in Rocket.toml to avoid seeing one request pop up every 2 seconds (this would not happen with a socket).
  2. The browser "network" inspection tools are full of requests (this would not happen with a socket).
  3. There is a delay of 2 seconds before the page reloads (or even more requests spamming the logs).
  4. I'm not using templates currently, but I think my logic will not work correctly there.

Alternative Solutions

Ideally, something would wrap around Rocket which keeps track of changes and keeps a socket open. Whenever Rocket then changes, it would notify the browser. I guess this could be something like nginx but then with some logic to track whether Rocket changed.

Additional Context

No response

System Checks

  • I do not believe that this feature can or should be implemented outside of Rocket.
  • I was unable to find a previous request for this feature.

This is more difficult than you might think. I'm not sure how npm's live-server works, but I suspect it's unloading and reloading the server, but keeping any existing steaming connections alive. For Rocket, because we aren't an interpreted language, when Rocket is restarted, the OS automatically resets every connection, so sockets would be disconnected in this case.

I think this could be implemented outside of Rocket, but it would require forking cargo watch to make it work. The idea is that it would open a Websocket Server on a different port (say 8001), and the client script would force reload the page when asked by the server. Injecting the client script automatically likely isn't worth the effort, especially since we don't know what each page is used for. I would suggest simply asking the users to inject it by hand, but a more automated solution might be possible via the webdriver spec.

From what I can think of, what you would do is create a faring that inserts into all the html some js that listens on a server sent event, then once the fairing shuts down, it tells the js to start polling the server, once the js receives a response from one of it's polls, it would reload the page and start over.

I'll try to make a crate for it when I get a chance!