New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Automatic OPTIONS responses were removed #284
Comments
You don't have to add a handler for OPTIONS on every endpoint. You have two options. The first one is to use the built-in plugin that restores the behavior you're looking for: server.use(restify.fullResponse()) The other (which I prefer since it affords more control) is to listen for the "MethodNotAllowed" event from the server like so: // This is a simplified example just to give you an idea
// You will probably need more allowed headers
function unknownMethodHandler(req, res) {
if (req.method.toLowerCase() === 'options') {
var allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version'];
if (res.methods.indexOf('OPTIONS') === -1) res.methods.push('OPTIONS');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', allowHeaders.join(', '));
res.header('Access-Control-Allow-Methods', res.methods.join(', '));
res.header('Access-Control-Allow-Origin', req.headers.origin);
return res.send(204);
}
else
return res.send(new restify.MethodNotAllowedError());
}
server.on('MethodNotAllowed', unknownMethodHandler); |
I should have mentioned there are line comments in d1366f2 that give more background on this. |
Ok, but the behavior of version 1.4 in regards to OPTIONS requests was very similar to the second option I posted. In fact I think I copied it from that version. Does it make so much difference to you that you have to define the unknown method handler function yourself to handle OPTIONS requests? If an endpoint does have a handler for OPTIONS this function will be overridden, so I think that covers all cases right? |
Nope, it doesn't make a difference to me. Yup, seems to handle the cases. Which is why I suggested it in the line comment. Basically this issue was added to bring awareness and because @mcavage is considering baking it back in & asked if I'd make an issue so this topic was tracked. If he decides to go the |
Yeah - this has already come up enough times in issues and offline emails to me that I'm going to make it "first class". I'm not sure yet what I want it to look like, but I'm probably going to have something like: |
Ok - I dropped support back into #master - I would actually like some feedback from you all on whether this is what you want, before I ship it out via NPM. Here's a (stupidly simple) sample, and semantics are explained below:
So, to get "simple/actual" request headers for CORS flying, you'll need to use
For For the example above, here's some sample curl request showing what happens: GET request with no Origin header (so no CORS):
Tack on Origin:
Here's a bad preflight request (note no access-control-request-method), so it 404s:
And a proper preflight request:
Play around some, and let me know how that gets on. |
Adding headers to the |
I think |
Small problem. Are you sure the access-control-request-headers header will always be separated with |
@ricardograca I believe this is what the RFCs/specs state, but I haven't checked. You can |
It might be worth adding |
Sorry for the delay, I was traveling. @benjie - this loop is out of scope of what is passed into @ricadograca/@benjie - they can be separated either way, I use this |
Okay, I'll take a look at doing that :)
My apologies, you're right of course - I got confused and added the unnecessary headers to my |
Closing this out, thanks for the catches! |
I'm sorry, but I'm still not able to get this working. Here is what I have. restify@2.1.1 // server.js // curl request // response Any idea what I could be doing wrong? |
One more thing... I'm trying to call my restify api using a backbone.js web app hosted on a remote server. It appears backbone attempts a preflight request, which then gets denied by my restify api with 405 error. I added the following to my venues.js route: // request headers from backbone.js app |
Feedback... The CORS headers should be removed from the full_response plugin. The preflight and simple request handling need to be better coordinated. It doesn't do well to have support for credentialed requests in one path but not the other. Or a knob for expose-headers but not accept-headers. What I've done, hacking away at the cors plugin, is to have it return both a "regular" handler and a preflight handler, used something like this: var cors = require('path/to/my-cors-plugin')({
credentials: true,
allow_headers: ['authorization'],
expose_headers: ['x-my-custom-header'],
});
restify_server.use(cors.simple);
restify_server.on('MethodNotAllowed', cors.preflight); I don't know if that is a direction you are interested in taking things, but it does provide a little better coordination between preflight and simple requests. |
Hi John, Yes, that actually does seem more sane, which is to bundle the preflight bits into the CORS plugin, instead of like it is now. I'll chip away at that as I get some time, unless somebody else (you? ;) ) wants to kick a PR over. |
Any news? I had to hack lib/router.js to get the access-control-allow-headers to include some custom ones I need sent. =/ |
@l8nite You were better off just creating your own custom unknownMethodHandler. |
That's what I ended up doing. Thanks for the example!
|
@ricardograca Thanks for sharing your solution! I'm calling services from dojo on another domain which is why I needed a solution. Your solution was receiving the options request and responding but my service was never called and in firebug I saw the request didn't finish loading. After some digging, I modified one line in your solution and it's working great! Here's the modified version in case it helps anyone else: function unknownMethodHandler(req, res) {
if (req.method.toLowerCase() === 'options') {
console.log('received an options method request');
var allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'Origin', 'X-Requested-With']; // added Origin & X-Requested-With
if (res.methods.indexOf('OPTIONS') === -1) res.methods.push('OPTIONS');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', allowHeaders.join(', '));
res.header('Access-Control-Allow-Methods', res.methods.join(', '));
res.header('Access-Control-Allow-Origin', req.headers.origin);
return res.send(204);
}
else
return res.send(new restify.MethodNotAllowedError());
}
server.on('MethodNotAllowed', unknownMethodHandler); |
We made correct handling for CORS and preflight requests with support for Cookies for authorization. # CORS headers
server.use (req, res, next) ->
res.header 'Access-Control-Allow-Origin', req.headers.origin if req.headers.origin
res.header 'Access-Control-Allow-Credentials', 'true'
res.header 'Access-Control-Allow-Headers', 'X-Requested-With, Cookie, Set-Cookie, Accept, Access-Control-Allow-Credentials, Origin, Content-Type, Request-Id , X-Api-Version, X-Request-Id'
res.header 'Access-Control-Expose-Headers', 'Set-Cookie'
next()
# Preflight requests
server.opts '.*', (req, res, next) ->
if req.headers.origin and req.headers['access-control-request-method']
res.header 'Access-Control-Allow-Origin', req.headers.origin
res.header 'Access-Control-Allow-Credentials', 'true'
res.header 'Access-Control-Allow-Headers', 'X-Requested-With, Cookie, Set-Cookie, Accept, Access-Control-Allow-Credentials, Origin, Content-Type, Request-Id , X-Api-Version, X-Request-Id'
res.header 'Access-Control-Expose-Headers', 'Set-Cookie'
res.header 'Allow', req.headers['access-control-request-method']
res.header 'Access-Control-Allow-Methods', req.headers['access-control-request-method']
req.log.info { url:req.url, method:req.headers['access-control-request-method'] }, "Preflight"
res.send 204
next()
else
res.send 404
next() |
released it to npm as se7ensky-restify-preflight |
Just for information, because it takes me a 2 hours to find this solution, if you use OAuth2, you must add 'Authorization' to headers of the Preflight response : function unknownMethodHandler(req, res) {
if (req.method.toLowerCase() === 'options') {
console.log('received an options method request');
var allowHeaders = ['Accept', 'Accept-Version', 'Content-Type', 'Api-Version', 'Origin', 'X-Requested-With', 'Authorization']; // added Origin & X-Requested-With & **Authorization**
if (res.methods.indexOf('OPTIONS') === -1) res.methods.push('OPTIONS');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Headers', allowHeaders.join(', '));
res.header('Access-Control-Allow-Methods', res.methods.join(', '));
res.header('Access-Control-Allow-Origin', req.headers.origin);
return res.send(200);
}
else
return res.send(new restify.MethodNotAllowedError());
}
server.on('MethodNotAllowed', unknownMethodHandler); |
updated se7ensky-restify-preflight, thanks |
I'm having an issue with POST requests with ,xhrFields: {withCredentials: true} and using se7ensky-restify-preflight. The preflight appears to be working on the OPTIONS, I can see
But then the POST request fails because:
So if I use credentials the cookie gets created and the POST fails, if I turn off the credentials, the cookie doesn't get created, and the POST succeeds, and I can see the header that the allow origin is ''. Am I mis-configuring (or not configuring) something? What am I missing here? I've updated to the latest versions of restify and preflight, nodejs v0.10.13 |
Please see example project: https://github.com/krava/se7ensky-restify-seed $ curl -X OPTIONS -H 'Origin: http://domain.tld:5000/' -H 'Access-Control-Request-Method: POST' -v http://localhost:3000/secure/check
$ curl -X POST -H 'Origin: http://domain.tld:5000/' -v http://localhost:3000/secure/check
Access-Control-Allow-Origin is always set to what I pass in Origin header. I cannot figure out how you received error about "Access-Control-Allow-Origin: *". Please give me right curl request, so I can debug that. Or give me code example that don't work. |
Short answer it's solved. (Thanks much for your help and library). The long answer is, either because I'm on a Windows cURL or I'm just not familiar with it, I couldn't build a good example. So I duplicated my code base to strip out the DB and REST code and create a simple example. That's when I found a duplicate set of calls, that apparently interfered with your preflight lib. These lines weren't in my server file, but in a configuration file it pulled in, so I didn't notice them before.
In case you're curious, this is the REST demo I created as a back end https://github.com/pcimino/nodejs-restify-mongodb. Currently working on an Enyo front end (not releasable yet) |
just find out the solution:
It works on my projects Http://yaha.me |
Found this issue after a few hours of googling and the CORS topic would definitely deserve its space in the doc where it is missing completely now. |
+1 to @lobodpav |
+1 @lobodpav |
@jfieber, is your customised CORS module in a shareable state? I think this is what makes the most sense too. After configuring As a small note: the Update: my attempt at a new spec-tested |
If your pre-flight THEN, these headers should be allowed in a subsequent CORS request:
If the requested headers are not allowed, then the
The response indicates that if the client was to send a request with the headers |
A recent checkin (d1366f2) stopped Restify's auto handling of
OPTIONS
requests. We now have the ability to create them manually.Although more control is good, manually adding
OPTIONS
for every endpoint is cumbersome and (if your application relies on CORS requests) can make it hard to move to Restify v2.I propose that the auto generated
OPTIONS
responses are "on by default" and developers can override the default by specifyingserver.opts(...)
. It would probably also be good to have a way to globally disable them.The text was updated successfully, but these errors were encountered: