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

Backport Accept check from chttpd #284

Closed
wants to merge 5 commits into from
Closed

Conversation

olafura
Copy link
Contributor

@olafura olafura commented Nov 16, 2014

Backport "Accept check" from chttpd to correctly handle when browser
try to access a database when they aren't logged in or need to change their login

This fixes so many bugs that have been littering the bug system for years.

The problem was that browsers over promise and we were using that to check
if it was json or html. I have another patch that is only 4 lines added and two
changed but since it was changed in chttpd I thought merging that was the best
thing.

Also if this patch doesn't get accepted I have an other solution in a form of allowing
rewrite to change headers.

This closes #283

COUCHDB-1175
COUCHDB-2435

try to access a database when they aren't logged in or need to change
their login
@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

Really strange errors, but I will look at them, CI worked for the 1.6.x branch.

@kxepal
Copy link
Member

kxepal commented Nov 16, 2014

Oh, don't waste your time on it - that's my fault. Just fixed it. Sorry for that.

@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

Thanks kxepal, no problem. I really like that Couchdb has a good CI, because there are so many moving parts.

@rnewson
Copy link
Member

rnewson commented Nov 16, 2014

hrm, but this code was ported to chttpd from couch_httpd originally...

@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

rnewson That's what the commit log says but it must of been cleaned up before that because I don't see it being used in couch_httpd. Maybe it's from rcouch?

@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

Here I found something: https://gitorious.org/refugeio/couch_core/commit/03ede5b

@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

There they are reverting from the correct behaviour to the incorrect behaviour. Using Mochi accepts_content_type to unroll the accept string which is really bad when you are trying to compare against two or more options.
https://github.com/mochi/mochiweb/blob/master/src/mochiweb_request.erl

@kxepal
Copy link
Member

kxepal commented Nov 16, 2014

Oh, so this is actually a rollback for some old commit?

@olafura
Copy link
Contributor Author

olafura commented Nov 16, 2014

kxepal, when I look at the old code then I see that the initial commit sort of worked. Then people added more to it so it broke completely. Mochi's accepts_content_type does everything correct but the problem is that the json check is before the html check and all web browsers promise to support basically everything with the q argument, so we get json when we should be getting a redirect. So yes it's a rollback but not just of one commit but many commits, that have tried to get the initial state back.

One thing to look at is maybe the default behaviour when not having accept should be json, but we would have to change that in both places.

I have a patch where I do this, instead of accepts_content_type:
check_header(MochiReq:get_header_value("Accept"), "application/json")
check_header(MochiReq:get_header_value("Accept"), "text/html")

check_header(undefined,_) ->
false;

check_header(Header, Value) ->
case string:str(Header, Value) of
0 ->
false;
_ ->
true
end.

But the problem might be the xhtml browsers. The most important thing is that it's consistent in both couch_httpd and couch_chttpd

@olafura
Copy link
Contributor Author

olafura commented Nov 17, 2014

Sorry I tend to complicate things with explanations :)

It's really simple, this patch is a rollback because bug. It also brings the 1.x.x series in line with what's in 2.0

This bug is one of the first ones I hit many years ago when I started using Couchdb, at the time I didn't know it was a bug. Just a really strange behavior, which I think is the case with most people. Futon and Fauxton have workarounds for this buggy behavior. People are using require_valid_user unnecessarily because of this with all it's Basic Authentication mess, having to close the browser to log out etc, and not being able to mix and match closed and open databases.

It's just a couple of lines of code that will make Couchdb much better and Couchapp are a lot better, just try it. Apply read protection on a database and visit it with this patch.

@kxepal
Copy link
Member

kxepal commented Nov 17, 2014

@olafura awesome! could you give me a case how to reproduce it? You can write etap test for it or just put here curl -v output before / after - I'll make a test on it for eunit.

@olafura
Copy link
Contributor Author

olafura commented Nov 17, 2014

kxepal This is my first etap so hopefully it's up to scratch.

@olafura
Copy link
Contributor Author

olafura commented Nov 17, 2014

It's funny it's the first one I can find to use SecObj, had to use ejson to get the structure right, I was very close.

@kxepal
Copy link
Member

kxepal commented Nov 27, 2014

+1, @rnewson what do you say?

@rnewson
Copy link
Member

rnewson commented Nov 28, 2014

This is reverting commit ecb23f5 which deliberately switched this behavior.

The timeline is;

a) the pre-ecb23f5af2 exists
b) Cloudant copies couch_httpd to their (private) chttpd repository (Mon Feb 7 14:48:59 2011)
c) ecb23f5 changes the logic (Mon May 23 06:39:47 2011)
d) chttpd is merged to couchdb

At no point does chttpd get the merge up from ecb23f5 for this change, so it is behind, rather than ahead of couch_httpd.

Whichever way we go, chttpd.erl and couch_httpd.erl should do the same thing. It's not clear which of the two behaviors that is.

-1 until this is resolved.

@kxepal
Copy link
Member

kxepal commented Nov 28, 2014

@rnewson oh yea, this is need to be fixed in the first place. @olafura how about to open issue about old vs new Accept behavior to track it on?

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

@rnewson That thing is that the change was a non feature, it added constraints to the pattern matching so it would pick up q= value, which breaks any comparison you can make. Because browsers say they support really anything with that value: http:https://stackoverflow.com/questions/10496570/what-is-the-purpose-of-the-q-values-in-the-http-accept-request-header.

Every framework it seems at some point has someone that adds support for those thing and everything breaks.

The reason why the initial patch didn't break badly at first is that it only did the comparison on text/html, which kind of works. But then people added it also to application/json, and added that first which totally broke everything.

The intentions were good and I have a patch which is stricter about matching it to text/html and application/json that can be merged. But please don't try to propagate this bug to chttp.

If you do a web search you can find a lot of bugs caused by respecting the q value plus wild card usage when doing comparisons. It's also an overkill, it does not add any value, just takes it away.

The browsers say with the Accept header, please bring us something in these format, but anything is also fine with us.

My test takes a real world use of the Accept header and most of the versions CouchDB fail it which is really bad.

There are three options:

  • Keep the patch like it is
  • Update the patch with stricter matching for the full application/json and text/html, which is in the comments.
  • Update the patch to select the quality of the matching, so to return the one the the client prefers, this is the most correct solution and I'll make a patch that implements this, if you choose so.

I'll also make patches for chttp for the two latter options if you choose that.

http:https://www.django-rest-framework.org/api-guide/content-negotiation/
http:https://shiflett.org/blog/2011/may/the-accept-header
http:https://docs.openstack.org/api/openstack-network/1.0/content/Request_Response_Types.html
PerlDancer/Dancer2#712
http:https://www.newmediacampaigns.com/blog/browser-rest-http-accept-headers
https://www.npmjs.org/package/contenttype //The q-value, if any, is ignored.
https://pypi.python.org/pypi/negotiator/1.0.0 //possibly a better way of doing it but I don't thing for this usecase needed
http:https://blog.bigbinary.com/2010/11/23/mime-type-resolution-in-rails.html
https://clojure-liberator.github.io/liberator/tutorial/conneg.html
https://docs.oracle.com/cd/E19776-01/820-4867/ghrpv/
http:https://mojolicio.us/perldoc/Mojolicious/Types.txt
https://parse.com/docs/js/symbols/express.Request.html

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

@kxepal Sorry I haven't yet learned the structure on how you do things, the only reason I submitted a pull request is that I saw that you were ok with that. I feel it's simpler than creating a bug report then submitting the patch and so on.

I can try make make some documentation on how this behavior works, I've read enough about it because of this bug :)

Is it really and open issue, I don't know. I don't think we do any more comparisons like that, but I can look. The links I sent fall into two categories "this q thing is weird and broken, let's ignore it" (easy option) or "let's try respecting the q value" (the more complex), also add a lot of test cases to make sure that it does not break anything.

At this point I'm just worried that people just see the complexity of decision and will just run away and this will bit rot.

The old version is pretty nice, if you include html in you accept then you get html otherwise you get json, do we really need anything more?

@rnewson
Copy link
Member

rnewson commented Nov 28, 2014

I'm all for improving this, I filed COUCHDB-1175 myself because neither the behavior before or after the original change here satisfied every use. That ticket, as you can see, ran aground without really solving the problem. I simply don't know if there's a solution that works correctly for all the cases. Is it your claim that there is, given that browsers send Accept with */* and usually without q values?

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

@rnewson It's usually sent with q= value but the problem is that the matching is not done together, so the correct behavior is giving the match two possible behaviors and letting it decide the best candidate. I'll make a patch this weekend. But I'm going to need somebodies help with making all the test cases, I saw that some other people had done those kinds of tests for their framework, it can be a starting place what we need to cover.

What bugs you about the old behavior, where you getting html when you didn't want that or were you getting json when you didn't want that. And what were the Accept headers that were causing you problems.

I don't know of a case myself where this old behavior would break, maybe except if you only accepted xhtml and not html, then why are you calling it or you want json if xhtml isn't available. All the browsers send something containing html and they are the primary use case for not sending json to.

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

Where are we returning text/plain, it might be good having the option of sending different responses based on preference but it's a much larger patch for more of the system, and still respect q value. Still doable.

The problem with the what this patch is trying to address is a very serious problem that CouchDB has and should be fixed as soon as possible. We have probably lost a lot of people over this, it's a really nasty bug.

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

I see that Mochi has an option of passing two arguments to the accepted_content_types so it can be the bases for the patch. It's going to be much more robust, but I'm wondering if we should be checking the functionality of the function or Mochi, I'll check the source to see how they test it.

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

I spoke too soon it doesn't decide just tells you that it supports both so maybe if it supports html then give them html otherwise not, should work.

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

Sorry about spamming but it does seem to try to do the right thing, but we should still decide if text/html or application/json comes first, because it uses that order when they are both equal.

@rnewson
Copy link
Member

rnewson commented Nov 28, 2014

That's the trick. If they're equal (which they usually are), then preferring html over json "breaks jquery" (and other libs I guess) and preferring json over html "breaks authentication".

@rnewson
Copy link
Member

rnewson commented Nov 28, 2014

I proposed this semantic on the ticket;

if Accept line is exactly "application/json", then send application/json, otherwise send text/html.

That puts a surmountable requirement on XHR calls and doesn't require browsers to send sensible Accept headers (which they probably never will).

At this point, someone from the Fauxton team should chip in, perhaps all this ugliness is really because of Futon and we can make a clean break?

@olafura
Copy link
Contributor Author

olafura commented Nov 28, 2014

Sound reasonable, if I remember right looking over the code, they have a fix for this incorrect behavior not the other way round. It takes a lot of code to check if it's alright to send people on their way instead of things just working. I think I something like that when I first encountered the bug. There are also so many cookie timeout things that just work when this behavior is right. Dealing with a 302 is very easy, just pipe the location to window.location.
http:https://stackoverflow.com/questions/6955308/in-an-ajax-call-302-is-not-followed

olafurara and others added 3 commits November 30, 2014 22:57
…the begining

since firefox kindly adds to the headers information it chooses at the end
per request from @rnewson
@olafura
Copy link
Contributor Author

olafura commented Dec 1, 2014

I'll look over the build failure tomorrow

@olafura
Copy link
Contributor Author

olafura commented Dec 1, 2014

If I'm reading this correctly it's a Latex error that I don't think has anything to do with my patch, and it's just in one version

@olafura
Copy link
Contributor Author

olafura commented Dec 1, 2014

I think I have to send in some patches to clean up some of the warnings, so it's easier to read through the log

@olafura
Copy link
Contributor Author

olafura commented Dec 4, 2014

I've now run the test of my own with kerl for the test that failed and it was successful for me.

@olafura
Copy link
Contributor Author

olafura commented Dec 10, 2014

@rnewson what do you think of the latest patch should I rework it towards using accepted_content_types or is this simple enough to work. I have the patch now in production on our service and it seems to work fine. My only complaint would not be with it but maybe we need a special use case for eventsource as I don't get the status code, then again I get an error which is basically the same thing. I just have to read an other standard now to see if we should be giving some other answer.

@olafura
Copy link
Contributor Author

olafura commented Dec 10, 2014

@kxepal I would also appreciate you opinion.

@skiqh
Copy link

skiqh commented Nov 10, 2015

@olafura I really appreciate you did this. Can you tell me if it went anywhere?

@kxepal
Copy link
Member

kxepal commented Nov 10, 2015

That awkward feeling when you miss notification and see it year after
@olafura sorry for that!
I think accepts_content_type is what have to be used. At least that's correct. For simple way, better use binary matching (after ?l2b cast for sure) instead of substring search, but that's a lyric.

Can you elaborate problem with eventsource if you didn't forget about?

@wohali
Copy link
Member

wohali commented Mar 19, 2017

Bump. @kxepal do you think you might be able to help out here?

@kxepal
Copy link
Member

kxepal commented Mar 19, 2017

@wohali Sure. Bookmarked.

@wohali wohali added this to the 1.7.0 milestone Apr 30, 2017
@janl
Copy link
Member

janl commented Oct 8, 2017

JIRA says wontfix

@janl janl closed this Oct 8, 2017
@janl janl added the wontfix label Oct 8, 2017
lag-linaro pushed a commit to lag-linaro/couchdb that referenced this pull request Oct 25, 2018
Error 'Command not found' when sname is used
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants