solidjs / solid-router

A universal router for Solid inspired by Ember and React Router
MIT License
1.1k stars 137 forks source link

Throwing or returning in redirect inside a cache function used in load results in error #456

Open cs-clarence opened 2 weeks ago

cs-clarence commented 2 weeks ago

Describe the bug

Given this code:

import { Title } from '@solidjs/meta';
import { RouteDefinition, cache, redirect } from '@solidjs/router';
import Counter from '~/components/Counter';

let isLoggedIn = false;

const redirectIfNotLoggedIn = cache(() => {
  if (!isLoggedIn) throw redirect('/login');

  return { data: 'test' };
}, 'redirect-if-not-logged-in');

export const route: RouteDefinition = {
  load: async () => await redirectIfNotLoggedIn(),
};

export default function Home() {
  return (
    <main>
      <Title>Hello World</Title>
      <h1>Hello world!</h1>
      <Counter />
      <p>
        Visit{' '}
        <a href="https://start.solidjs.com" target="_blank">
          start.solidjs.com
        </a>{' '}
        to learn how to build SolidStart apps.
      </p>
    </main>
  );
}

When returning a redirect from a cache function, this error occurs:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at __node_internal_captureLargerStackTrace2 (https://yqniqqmaogithub-cd2c.w-corp-staticblitz.com/builtins.5bf3667c.js:101:5335)
    at new NodeError (https://yqniqqmaogithub-cd2c.w-corp-staticblitz.com/builtins.5bf3667c.js:101:4149)
    at ServerResponse.setHeader (https://yqniqqmaogithub-cd2c.w-corp-staticblitz.com/builtins.5bf3667c.js:6:8796)
    at setResponseHeader (file:///home/projects/yqniqqmao.github/node_modules/h3/dist/index.mjs:908:18)
    at Module.eval (/home/projects/yqniqqmao.github/node_modules/vinxi/runtime/http.js:149:12)
    at eval (/home/projects/yqniqqmao.github/node_modules/@solidjs/start/dist/server/handler.js:101:29)
    at doShell (file:///home/projects/yqniqqmao.github/node_modules/solid-js/web/dist/server.js:358:7)
    at Object.onDone (file:///home/projects/yqniqqmao.github/node_modules/solid-js/web/dist/server.js:221:5)
    at Te.flush (file:///home/projects/yqniqqmao.github/node_modules/seroval/dist/esm/production/index.mjs:61:38999)
    at eval (file:///home/projects/yqniqqmao.github/node_modules/solid-js/web/dist/server.js:240:42) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

Node.js 18.20.3

When throwing a redirect:

[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "[object Response]".] {
  code: 'ERR_UNHANDLED_REJECTION'
}

Node.js 18.20.3

Your Example Website or App

https://stackblitz.com/edit/github-3nqamc?file=src%2Froutes%2Findex.tsx

Steps to Reproduce the Bug or Issue

  1. Run the application
  2. Visit the index route

Expected behavior

Returning/throwing a redirect in a cache function should redirect

Screenshots or Videos

No response

Platform

Additional context

No response

ryansolid commented 1 week ago

Likely it's because the server has already responded at the point that it resolves. If there is no async in your components then the server is going to thing it is done (or at least can stream the first chunk).. in which case by the time it tries to process the redirect it thinks it is done. There might be still a timing bug/missing guard here but we probably need to find a reproduction that reads the routes data.