routing-controllers icon indicating copy to clipboard operation
routing-controllers copied to clipboard

question: It does't work with express-mysql-session

Open Aki-7 opened this issue 4 years ago • 0 comments

I was trying to...

use this library with express-mysql-session, but it doesn't work.

The problem:

The error occurs when...

  • Run the code below.
  • Access to localhost:3000 (Got a session-id)
  • Access to localhost:3000/secret

then I expect to get an Unauthorized Error(401) but get a net::ERR_CONTENT_LENGTH_MISMATCH

import "reflect-metadata";
import express from "express";
import session from "express-session";
import MySQLStoreClassFactory from "express-mysql-session";
import {
  Authorized,
  Controller,
  Get,
  Session,
  useExpressServer,
} from "routing-controllers";

const PORT = 3000;
const SECRET = "secret";
const DB_HOST = "localhost";
const DB_PORT = 3306;
const DB_USER = "root";
const DB_PASS = "password";
const DB_NAME = "express_session";

const app = express();

const MySQLSessionStore = MySQLStoreClassFactory(session as any);
const sessionStore = new MySQLSessionStore({
  host: DB_HOST,
  port: DB_PORT,
  user: DB_USER,
  password: DB_PASS,
  database: DB_NAME,
});

@Controller()
class IndexController {
  @Get("/")
  index(@Session() sess: any) {
    sess.views = (sess.views || 0) + 1;
    return "Hello World";
  }

  @Authorized()
  @Get("/secret")
  secret() {
    return "Secret Page";
  }
}

app.use(
  session({
    secret: SECRET,
    store: sessionStore,
    resave: false,
    saveUninitialized: false,
  })
);

useExpressServer(app, {
  controllers: [IndexController],
  authorizationChecker: () => false,
});

app.listen(PORT, () => {
  console.log(`Server is listening on port ${PORT}`);
});

I think this is because...

https://github.com/typestack/routing-controllers/blob/24556885e58e1022531881eecb963c363e9933a9/src/driver/express/ExpressDriver.ts#L384-L391

Here, the next callback is called after the res.send method was called. (ref #589)

the res.send method calls res.end internally and when using express-session library, res.end method is proxied.

In this proxy, the first ${chunk.length - 1} bytes of the data chunk will be written immediately but the remaining one byte will be written after saving/touching session data to the data store asynchronously.

ref https://github.com/expressjs/session/blob/a8641429502fcc076c4b2dcbd6b2320891c1650c/index.js#L334-L343

if (shouldSave(req)) {
  req.session.save(function onsave(err) {
    if (err) {
      defer(next, err);
    }

    writeend();
  });

  return writetop();
} else if (...) {

On the other hand, when the next callback is called with an error, the error will be passed to this method.

In this method, if the headers of the response have already been sent, the socket of the response will be destroyed. ref https://github.com/pillarjs/finalhandler/blob/15e78cab32ecbd4993d1575a065963b238336df9/index.js#L126-L130

So, when the socket is destroyed before the last byte of the data chunk is written, the CONTENT_LENGTH_MISMATCH error happens. (actually, content length was mismatched by 1 byte)

As mentioned in #589, the next callback seems strange.

But this issue is about a very ordinary use case, so I feel there is some best practice I don't know.

So, I want to know how to set up this amazing library with express-mysql-session if you have any idea.

Aki-7 avatar Oct 17 '20 16:10 Aki-7