Home > database >  Where are OPTIONS requests handled in express?
Where are OPTIONS requests handled in express?

Time:12-29

I was testing get and post requests in jsfiddle to better understand cors and csrf.

fetch('http://localhost:5000/auth')
  .then((response) => response.json())
  .then((data) => {
    console.log('Success:', data);
  })
  .catch((error) => {
    console.error('Error:', error);
  });

chrome console get request

Where in the code is an OPTIONS request handled with a status code of 200? I don't have any catch all middleware.

const app = express()

//Listen
//Connect to database

//Middlewares
app.use(express.json())

//Routes
app.use('/auth', require('./routes/auth'))
const router = express.Router()

router.get('/', async (req, res) => {
  console.log('test')
  res.status(202).send(await User.find())
})

router.post('/signup', async (req, res) => {
})

module.exports = router

I am having a hard time finding an OPTIONS route in the enter image description here

enter image description here

For context, when I added enter image description here

enter image description here

Edit 3: Used a simple webpage instead of jsfiddle:

<script>
    const data = { name: 'example', password: 'password'};

    fetch('http://localhost:3001', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    })
      .then((response) => response.json())
      .then((data) => {
        console.log('Success:', data);
      })
      .catch((error) => {
        console.error('Error:', error);
      });
  </script>

No preflight happens for get requests

enter image description here , but one does happen for a post request: enter image description here

enter image description here

CodePudding user response:

Where in the code is an OPTIONS request handled with a status code of 200? I don't have any catch all middleware.

Express apparently does have code that will, in some circumstances, send a 200 response back from an OPTIONS request. That code is here and here in the Express repository for further context. These two snippets of Express code are shown below here:

  // for options requests, respond with a default if nothing else responds
  if (req.method === 'OPTIONS') {
    done = wrap(done, function(old, err) {
      if (err || options.length === 0) return old(err);
      sendOptionsResponse(res, options, old);
    });
  }

And this (which sends a 200 response):

function sendOptionsResponse(res, options, next) {
  try {
    var body = options.join(',');
    res.set('Allow', body);
    res.send(body);
  } catch (err) {
    next(err);
  }
}

I found this by inserting this middleware into your Express server code and then setting a breakpoint inside the middleware and then stepping through the call to next() to see exactly what Express does in this case. And, low and behold, it eventually gets to sendOptionsResponse() and sends a 200 response for some OPTIONS requests.

// debugging middleware, should be first router handler of any kind
let requestCntr = 0;
app.use((req, res, next) => {
    let thisRequest = requestCntr  ;
    console.log(`${thisRequest}: ${req.method}, ${req.originalUrl}, `, req.headers);
    // watch for end of theresponse
    res.on('close', () => {
        console.log(`${thisRequest}: close response, res.statusCode = ${res.statusCode}, outbound headers: `, res.getHeaders());
    });
    next();
});

FYI, you can use this above middleware to see exactly what is arriving on your server and in what order.

For your particular jsFiddle this is what I see:

Listening on port 3001
0: OPTIONS, /,  {
  host: 'localhost:3001',
  connection: 'keep-alive',
  pragma: 'no-cache',
  'cache-control': 'no-cache',
  accept: '*/*',
  'access-control-request-method': 'POST',
  'access-control-request-headers': 'content-type',
  'access-control-request-private-network': 'true',
  origin: 'https://fiddle.jshell.net',
  'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
  'sec-fetch-mode': 'cors',
  'sec-fetch-site': 'cross-site',
  'sec-fetch-dest': 'empty',
  'accept-encoding': 'gzip, deflate, br',
  'accept-language': 'en-US,en;q=0.9'
}
0: close response, res.statusCode = 200, outbound headers:  [Object: null prototype] {
  'x-powered-by': 'Express',
  allow: 'GET,HEAD,POST',
  'content-type': 'text/html; charset=utf-8',
  'content-length': '13',
  etag: 'W/"d-bMedpZYGrVt1nR4x qdNZ2GqyRo"'
}

You can see there is exactly one inbound request to the server, an OPTIONS request. The express server (on it's own sends a 200 status, but only allows text/html as the content-typefor a GET, HEAD or POST. Since the jsFiddle wants to sentcontent-type: application/json`, the browser fails the request as a CORS violation.


NOTE and further explanation

As a point of confusion, the Chrome inspector (network tab) in the browser are showing more requests than actually show up on the server so you are being deceived by what Chrome is showing. When I run your client-side code in a JSFiddle, I get ONLY one OPTIONS request send to the server and NO other requests, even though the Chrome inspector shows more than that. What I think is happening is that Chrome sees the POST request as being content-type: application/json so it needs pre-flight. It sends the OPTIONS request as the pre-flight, it gets back a 200 response, but does NOT get back the headers it needs to allow that specific POST request to be sent so it doesn't send it.

When in doubt, instrument your server and see exactly what it is actually receiving.

Chrome deprecating direct access to private network endpoints

See this article. This may also be a reason why the public website jsFiddle can't access localhost or why there are extra warnings in the Chrome inspector. This is a fairly new restriction (in the last year). I've seen some other sites have to remove features because of this. For example, Tivo had to remove the ability to watch saved Tivo recordings (which are served from a local http server) from Chrome even when on my own local network. You can now only do that from an app.

  • Related