Skip to content
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

Chrome Update 52: Response for preflight has invalid HTTP status code 405 #111

Closed
Mythli opened this issue Jul 22, 2016 · 31 comments
Closed

Comments

@Mythli
Copy link

Mythli commented Jul 22, 2016

My coworker just updated to Chrome 52 automatically.

After a lot of debugging we found out that this update apparently has an issue with pouchdb-authentication.

How to reproduce:

Use the login method of a remote db. This will result in the following exceptions:

index-browser.js:219 OPTIONS http://couchdb.garedu.com/_session xhRequest @ index-browser.js:219ajax$1 @ index-browser.js:238ajaxCore @ index-browser.js:332ajax @ index-browser.js:389(anonymous function) @ index.js:92(anonymous function) @ utils.js:66(anonymous function) @ utils.js:54(anonymous function) @ utils.js:35loginRemote @ DatabaseManager.js:116(anonymous function) @ DatabaseManager.js:150F @ _export.js:35initUserDbs @ DatabaseManager.js:147LoginView._this.login @ AuthenticationPage.jsx:47ReactErrorUtils.invokeGuardedCallback @ ReactErrorUtils.js:70executeDispatch @ EventPluginUtils.js:89executeDispatchesInOrder @ EventPluginUtils.js:112executeDispatchesAndRelease @ EventPluginHub.js:44executeDispatchesAndReleaseTopLevel @ EventPluginHub.js:55forEachAccumulated @ forEachAccumulated.js:25processEventQueue @ EventPluginHub.js:221runEventQueueInBatch @ ReactEventEmitterMixin.js:18handleTopLevel @ ReactEventEmitterMixin.js:29handleTopLevelImpl @ ReactEventListener.js:73perform @ Transaction.js:138batchedUpdates @ ReactDefaultBatchingStrategy.js:63batchedUpdates @ ReactUpdates.js:98dispatchEvent @ ReactEventListener.js:150 localhost/:1 XMLHttpRequest cannot load http://couchdb.garedu.com/_session. Response for preflight has invalid HTTP status code 405 reducers.js:16 new gareduMiner state: Object {settings: Object, status: Object, session: Object}

Would love to see if someone has the same issue.

As of now I do not know which change in Chrome breaks authentication. What I know is that replication with a database that does not need authentication still works fine.

In FF everything works fine as well. Downgrading Chrome alsow works.

I lack the in depth knowledge of PouchDB to efficiently fix this issue. I would love if someone would look into it.

Best regards

@nkinar
Copy link

nkinar commented Jul 22, 2016

Yes, I can also confirm that this is a bug on Chrome 52.0.2743.82 m, running on Windows 10 (64-bit). Firefox (47.0.1) also on the same platform works well, so this appears to be an issue with Chrome.

@ccapndave
Copy link

I've been looking into this a lot, and the problem is that as of Chrome 52, there is an empty Access-Control-Request-Headers header being sent along with the OPTIONS request. This causes CouchDB to deny the request as it can't parse the contents (https://github.com/apache/couchdb-chttpd/blob/ab299ebfcb833a819ccc9ddff3a2074ee25e0523/src/chttpd_cors.erl#L102).

@ccapndave
Copy link

ccapndave commented Jul 23, 2016

As a workaround, you can install nginx as a reverse proxy in front of CouchDB and get it to strip out the header. You would then access CouchDB on 6984 instead of 5984 (this config also implements SSL):

server {

  listen 6984 ssl;

  server_name myserver.com;
  ssl_certificate /whereveriputmykeys/myserver.crt;
  ssl_certificate_key /whereveriputmykeys/myserver.key;

  location / {
    proxy_set_header Access-Control-Request-Headers "";
    proxy_pass http://127.0.0.1:5984;
  }

}

Many thanks to trolldx on the #couchdb Freenode channel for taking the time to help me figure out this annoying issue 👍

@nkinar
Copy link

nkinar commented Jul 23, 2016

Thanks, ccapndave. I am wondering if something can be done on our end (pouchdb) or with the CouchDB code? If Chrome is going to always exhibit this behavior, then perhaps something could be done?

@ccapndave
Copy link

ccapndave commented Jul 23, 2016

The CouchDB code can certainly be changed to accept an empty header here (although not by me!) I'm not quite sure if anything can be done from the client side. Its possible that using the Fetch API to do the requests might have different/more reliable behaviour? Its also possible that its actually a Chrome bug and will be fixed at some point.

@nkinar
Copy link

nkinar commented Jul 23, 2016

Sounds good ccapndave . In addition, if this can't be fixed in the long term, then maybe your nice solution could be added to a wiki page somewhere (i.e. for pouchdb-authentication or for PouchDB). Maybe someone from the couchdb project would be interested in patching this. I've tested the bug on Google Chrome Canary and it also occurs on this browser as well.

@Mythli
Copy link
Author

Mythli commented Jul 25, 2016

Good that another workaround has been found.

We worked around the issue through switching from http authentication to token based one.

@nolanlawson
Copy link
Member

Hi all, if there is a bug in CouchDB, could you please file it on the CouchDB repo? They are about to release 2.0, so maybe they will have time to fix it: https://issues.apache.org/jira/browse/COUCHDB/?selectedTab=com.atlassian.jira.jira-projects-plugin:summary-panel

@nolanlawson
Copy link
Member

Alternatively, you could file the bug on Chromium, but I'm not sure what the W3C specs say about how Access-Control-Request-Headers should behave.

@nkinar
Copy link

nkinar commented Jul 27, 2016

Either CouchdB or Chromium sounds good, Nolan - I'm not certain how the bug could be filed. Maybe someone subscribed to this thread could do this. Thanks for your fine work on the Pouchdb code.

@ccapndave
Copy link

BTW - this problem goes beyond pouchdb-authentication; I have now experienced it in normal pouchdb usage (when getting bulk_docs).

@nolanlawson
Copy link
Member

@nolanlawson
Copy link
Member

It sounds like this is a bug in CouchDB because Chrome is adhering to the letter of the law (i.e. the spec allows for sending an empty header) but CouchDB can't parse it.

@nolanlawson
Copy link
Member

Can someone please provide a very simple test case to reproduce this? I am unable to reproduce using Chrome 52 on a Mac.

The very best test here would be a Docker script to run the right version of CouchDB, then an HTML page containing PouchDB and demonstrating the error. Right now I'm not sure what steps I need to do to reproduce this.

@Mythli
Copy link
Author

Mythli commented Aug 1, 2016

Hey Nolan, unfortunately I have no experience with docker but can provide you with remote login to my couchdb where the error occurs.

Server: Ubuntu 14.04.4 LTS
CouchDb Version: Apache CouchDB 1.6.1

I installed couchdb using the third party ppa "ppa:couchdb/stable"

Url: couchdb@garedu.com
User: nolan@garedu.com
Password: 123456

Hope this information helps resolving the issue.

@willholley
Copy link

Reproduced using curl:

$ curl 'http://couchdb.garedu.com/_session' -H 'Accept:*/*' -H "Accept-Encoding:gzip, deflate, sdch, br" -H "Accept-Language: en-US,en;q=0.8;de;q=0.6" -H "Access-Control-Request-Headers;" -H "Access-Control-Request-Method: POST" -H "Origin:http://somewhere.com" -H "Cache-Control: no-cache" -H "DNT: 1" -H "Host:somewhere.com" -H "Pragma:no-cache" -H"Referer: http://somewhere.com" -XOPTIONS -v -H"User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2787.0 Safari/537.36" --compressed
Mon  1 Aug 2016 12:23:07 BST
*   Trying 144.76.61.103...
* Connected to couchdb.garedu.com (144.76.61.103) port 80 (#0)
> OPTIONS /_session HTTP/1.1
> Host:somewhere.com
> Accept:*/*
> Accept-Encoding:gzip, deflate, sdch, br
> Accept-Language: en-US,en;q=0.8;de;q=0.6
> Access-Control-Request-Headers:
> Access-Control-Request-Method: POST
> Origin:http://somewhere.com
> Cache-Control: no-cache
> DNT: 1
> Pragma:no-cache
> Referer: http://somewhere.com
> User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2787.0 Safari/537.36
> 
< HTTP/1.1 405 Method Not Allowed
< Server: nginx/1.8.0
< Date: Mon, 01 Aug 2016 11:23:07 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 76
< Connection: keep-alive
< Cache-Control: must-revalidate
< Allow: GET,HEAD,POST,DELETE
< Access-Control-Expose-Headers: Cache-Control, Content-Type, Server
< Access-Control-Allow-Origin: http://somewhere.com
< Access-Control-Allow-Credentials: true
< 
{"error":"method_not_allowed","reason":"Only GET,HEAD,POST,DELETE allowed"}
* Connection #0 to host couchdb.garedu.com left intact

@willholley
Copy link

@willholley
Copy link

and a PR at apache/couchdb-chttpd#135. I'd guess this is a low priority for CouchDB so please comment on the Jira ticket if it's causing you problems.

@nolanlawson
Copy link
Member

Maybe we should file an issue on Chrome? It's a better solution for Chrome to omit the header and fix this issue in v53 rather than expect CouchDB to fix it in 1.7/2.0 and then have everybody running CouchDB upgrade their servers.

@nolanlawson
Copy link
Member

nolanlawson commented Aug 2, 2016

I've filed an isuse on Chromium: https://bugs.chromium.org/p/chromium/issues/detail?id=633729 .

As of right now, I don't see any way for PouchDB or pouchdb-authentication to fix this issue. It seems that the fix either needs to go in Chrome or in CouchDB. I'd be very happy if anyone can think of a workaround, though.

FWIW this issue can be reproduced in pouchdb-authentication's own test suite, which is how I managed to reproduce it. The test suite still passes in Firefox and Edge, but not Chrome 52.

@fgrs
Copy link

fgrs commented Aug 4, 2016

I'm on the same boat :/

@ionrocks
Copy link

ionrocks commented Aug 6, 2016

I found a workaround for my use case, so even though it's incredibly hacky, I thought I'd share it.

The first step is to edit your CouchDB configuration. I added the custom x-hello header to my cors.headers. Why x-hello? Because I'm pretty sure it isn't used for anything important.

Now, for any calls that are having this issue, you just need to pass in some ajax options:

{
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
}

Per-Database

Getting that pesky 405 a lot? In theory, this should fix the whole database! Unfortunately, PouchDB Authentication ignores these ajax options, so if your woes are auth-specific, you'll have to sprinkle in some of the per-call magic in too.

var db = new PouchDB('http://localhost:5984/mydb', {
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
})

Per-Call

Don't want to include a useless header all the time? Just add it when you need it! Most PouchDB calls take an optional options argument.

db.login('myuser', 'mypass', {
  ajax: {
    headers: {
      'X-Hello': 'World'
    }
  }
})

The Crazy Forking Option

It took a silly amount of debugging before I realized that PouchDB Authentication ignored the ajax options for the associated database. Since I didn't want to pass in options all the time, I forked PouchDB Authentication. To get the db.login() method working (it was the one giving me 405 grief), I only had to change one line. I stopped meddling after that.

// Module: pouchdb-authentication@0.5.3
// File: lib/index.js
// Line: 91

// Before
}, opts.ajax || {});

// After
}, opts.ajax || {}, db.__opts.ajax || {});


Note: I've only tested this on Chrome 52.0.2743.116 on Mac 10.10.5. Your mileage may vary.

@nolanlawson
Copy link
Member

@ionrocks Based on your "crazy forking option," it looks like this could be fixed by adding an explicit X-Hello header to each ajax request. Perhaps we should do that both here and in PouchDB's core pouchdb-adapter-ajax module..

BTW the fact that PouchDB Authentication ignores the ajax options is a bug; would be happy if someone would submit a PR to fix it.

@ionrocks
Copy link

ionrocks commented Aug 7, 2016

The downside of X-Hello is that people would have to reconfigure their CouchDB installs, or the preflights will (legitimately) fail.

I want to experiment (when I'm back in the office) and see if Chrome is excluding common headers from Access-Control-Request-Headers or if the same idea could be applied more elegantly by, for example, ensuring that every request includes an Accept header. Since it's one of the ones included by add-cors-to-couchdb, it would fix the issue with no reconfiguration required.

As for ignoring the ajax options, I figured they had caused issues in the past and were separate on purpose. 😄 I'll see about making a real PR this week, and maybe even poke at some of the other help wanted issues.

@jjal
Copy link

jjal commented Aug 7, 2016

We've hacked around this using the following nginx reverse proxy config. We tried @ccapndave's header pass but weren't able to get replication working, so decided to take the kitchen sink approach. This seems to be working for now. Eager to see couchdb fix this, given that we have no control over users' browser updates.

location / {
      proxy_pass http://couchdb:5984;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

      if ($request_method = OPTIONS ) {
        add_header 'Access-Control-Allow-Origin' '$http_origin';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, PUT, POST, HEAD, DELETE, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Origin,Referer,X-Csrf-Token,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
      }
    }

jlami added a commit to martinic/ember-simple-auth-pouch that referenced this issue Aug 9, 2016
@asanchez75
Copy link

meanwhile, for Mac users, an alternative is to use Chromium 51 http://www.freesmug.org/chromium

@skiqh
Copy link
Contributor

skiqh commented Aug 26, 2016

I recently stumbled accross this thing as well and have been playing around, trying to find a way to get around it. It seems to me that sending the login credentials as JSON already fixes the problem; it'd be nice if someone could confirm or refute this solution.

I have no idea why it actually makes a difference, but anyway, here we go:

// File: lib/index.js
// modify lines 89 and 90

86:   var ajaxOpts = utils.extend(true, {
87:     method : 'POST',
88:     url : utils.getSessionUrl(db),
89: *   headers : {'Content-Type': 'application/json'},
90: *   body : JSON.stringify({name: username, password: password})
91:   }, opts.ajax || {});
92:   utils.ajax(ajaxOpts, wrapError(callback));

Edit: tested in Chrome 52.0.2743.116 m, Windows 10, 1607
Still working in: FF 47.0.1 and Edge 38.14393.0.0

Thanks by the way for all your work, everybody!

@timwis
Copy link

timwis commented Aug 28, 2016

I'm getting this too -- quite odd, wasn't getting it yesterday. Could it be related to this issue?

@jlami
Copy link

jlami commented Aug 29, 2016

Yes, submitting as JSON will send the header Access-Control-Allow-Headers: content-type which will do the same as the X-Hello: World did before. But the benefit of the Content-Type header is that it will also work with Cloudant. Cloudant will not accept the X-Hello header. So for the temporary fix my vote goes out to #115

@dlucian
Copy link

dlucian commented Aug 31, 2016

@jlami Your Content-Type fix works for me too, I'm using Cloudant so there's no chance in changing server configuration. Thank you!

@nolanlawson
Copy link
Member

Fixed by #115

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests