Problem/Motivation
History: #1572394: Language detection by domain only works on port 80
URL generation does not works when the server listen to port non 80 and language detection is domain based.
The port number missing from the generated urls, as generated by language_url_rewrite_url.
The cause is that $options['base_url'] only contains the domain name without protocol and port, see #1572394: Language detection by domain only works on port 80 and #1250800: Language domain should work regardless of ports or protocols
Proposed resolution
Remaining tasks
Backport the changes to D7. Some of the code changes have already been backported in #78, but not the tests. See #79. The tests also need to be backported and a tests-only patch needs to be provided.
User interface changes
API changes
Original report by @Sweetchuck
url() function invoke the hook hook_url_outbound(). drupal_alter('url_outbound', $path, $options, $original_path);
Module locale implements this hook locale_url_outbound_alter() and call some configured callbacks.
In this case the callback function is locale_language_url_rewrite_url(), which is trim the port number from domain.
$host = 'http://' . str_replace(array('http://', 'https://'), '', $options['language']->domain);
$host = parse_url($host, PHP_URL_HOST);
I had replace this line
$options['base_url'] = $url_scheme . $host;
to
$options['base_url'] = $url_scheme . $host;
if (($port = parse_url($options['language']->domain, PHP_URL_PORT))) {
$options['base_url'] .= ":$port";
}
It seems to good
| Comment | File | Size | Author |
|---|---|---|---|
| #94 | url_generation_only-1645156-94-tests-only.patch | 2.33 KB | talhaparacha |
| #94 | url_generation_only-1645156-94.patch | 4.21 KB | talhaparacha |
| #89 | url_generation_only-1645156-89.patch.patch | 2.33 KB | talhaparacha |
| #78 | drupal--i1645156-78.D7.patch | 1.87 KB | amontero |
| #75 | drupal--i1645156-74.D7.patch | 1.98 KB | amontero |
Comments
Comment #1
gábor hojtsyDoes this also apply to Drupal 8?
Comment #2
gábor hojtsyWill also definitely need tests.
Comment #3
attiks commentedTagging as release blocker so this gets fixed first
Comment #4
attiks commentedProblem confirmed on Drupal 8
Setup: Server listening on port 80 and 81
All URL's are linking to port 80
Comment #5
gábor hojtsyComment #6
attiks commentedLet's try this
Comment #8
attiks commentedfixed
Comment #9
attiks commenteddots
Comment #10
attiks commentedComment #11
tstoecklerLooks good, except for these code-style issues.
This is missing a space before the =.
This is a matter of taste, but the first line could be written as
That would allow using $port directly and would avoid the arbitrary 1 index.
We usually prefer single-quotes, I think.
Comment #12
attiks commentedspace and quotes fixed
$http_host_tmp[1] rewritten to end($http_host_tmp), a similar construct is used in the other issue.
Comment #13
gábor hojtsy@attiks: can you summarize in a couple sentences the reasons this did not work before?
Comment #14
tstoecklerCode-style-wise looks good. :)
Comment #15
David_Rothstein commentedIs this tagged " 7.15 release blocker" because it's a regression from behavior that worked in previous versions of Drupal? Do we know when the last time was (if ever) that this behavior did work?
Just trying to clarify why that tag is applied to this issue... thanks.
Comment #16
attiks commentedThis was first committed to D7 on 27/3/2012 #1250800-130: Language domain should work regardless of ports or protocols, and release with 7.12
Followed by #1572394: Language detection by domain only works on port 80 on 15/5/2012 with a first problem, fixed and committed #1572394-43: Language detection by domain only works on port 80
And finally (i hope) this issue
It's not really a regression but in the past you couldn't use domain names if you site needed http/https, we tried fixing it in the first issue, the other 2 are 'new' problems. So it would be nice if we can fix this before releasing a new D7.
Comment #17
gábor hojtsyWell, it is both a regression, and not. So earlier before #1250800: Language domain should work regardless of ports or protocols, your help text said you should enter a full URL (with protocol, port, etc) which was prepended to your paths for specific languages. That functionality still works in Drupal 7, since we wanted to keep it backwards compatible, but the current help text suggests you enter a domain name only, and Drupal will put on the port and protocol itself, so you can have http and https pages for example and still use domains for languages. If you do follow the new instructions, custom ports will not work. If you follow the original (old) instructions, custom ports will work. So its a regression in that our current instructions will not make your setting work if you are not on port 80, looks like.
Comment #18
David_Rothstein commentedOK, thanks for the further information.
I think since this has been around for a little while and is only a "pseudo-regression", we probably wouldn't hold up a Drupal 7 release just for this, so I'm removing the tag. But I'd definitely commit this to D7 quickly, once it's ready!
Comment #19
gábor hojtsy#12: i1645156-12.patch queued for re-testing.
Comment #20
gábor hojtsyGenerally looks good, great that it has extra test coverage. Some notes:
I have not found docs that would indicate this would only ever contain the number if its different from 80 (or that it would always contain the number in that case). Looked on php.net. Do we have some docs to refer to here?
"when using a domain name with a non-standard port" right? That is the goal of the patch. (Note the comment line will pass 80 chars then, needs some word-smithing to stay under that limit).
This is more minor, but it would be good to use a domain that is relevant for the language tested :) You test French with example.cn, interesting combination :)
Comment #21
attiks commented$_SERVER['HTTP_HOST'] is the Host taken from the HTTP request header, it can even contain :80 if the request was made like that, see a similar discussion in secure pages: #334419: $_SERVER['HTTP_HOST'] has port number and regex doesn't strip it.. php.net says "Contents of the Host: header from the current request, if there is one."
I used example.cn because the tests are using this already for a different test, if you want I'll change it.
I will fix the comment later today.
Comment #22
gábor hojtsyOk, http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23 documents the port question pretty well, right. Would be good to summarize it in a sentence there. "HTTP_HOST will optionally contain the port. Preserve that for the domain of the target language as well." Or something like that. It would be good to fix the example.cn test and the comments too, thanks! I think this looks to be close.
Comment #23
attiks commentedshould be ok
Comment #24
gábor hojtsyLooks great now. Fixes the bug, expands tests proper :) Thanks for sticking to it.
Comment #25
gábor hojtsyAdd on sprint for tracking.
Comment #26
webchickThis seems like a pretty straight-forward bug fix. Nice test!
Committed and pushed to 8.x. Doesn't seem to apply cleanly to D7.
Comment #27
albert volkman commentedReworked test and test works properly locally. I'm somewhat of a noob when it comes to testing, so please let me know if I did anything incorrectly.
Comment #29
attiks commentedremove this please
Comment #30
albert volkman commentedAh, that's what I get for trying to code in a car while low on sleep, lol. Thanks @attiks!
Comment #31
David_Rothstein commentedShouldn't we be undoing this at the end of the test? Otherwise, that value will persist for all other tests that run afterwards in the same page request, which could potentially affect some of them.
I can't think of a specific example right now, but I know that elsewhere in the codebase where we have a test modify $_SERVER, we try to make sure the modifications are undone at the end.
Comment #32
attiks commentedI had a quick look:
I don't think it's needed, but it wouldn't hurt to reset the value,
should do it
But changing this will mean fixing Drupal 8 first
Comment #33
gábor hojtsyAgreed, let's fix it for Drupal 8 first. I think we can just remember the value at the start and set it back at the end? Instead of the dynamic manipulation suggested.
Comment #34
attiks commentedComment #35
gábor hojtsyTrivial.
Comment #36
David_Rothstein commentedYup.
This is one of the quickest RTBCs ever, I think :) (Assuming the tests pass.)
Comment #37
David_Rothstein commentedOh, ha, mine wasn't the quickest RTBC ever because Gábor cross-posted and changed the issue status one minute before me :)
Comment #38
webchick*whoosh*
Committed and pushed to 8.x. Thanks! :)
Comment #39
gábor hojtsyLet's continue with D7 backport then.
Comment #40
attiks commentedas requested
Comment #41
gábor hojtsyJust looking at this one, is it not possible at all for $options['base_url'] to already contain a port? (In which case after this patch it will be two ports?).
Comment #42
attiks commentedAccording to default.settings.php it can be
* Examples:
* $base_url = 'http://www.example.com';
* $base_url = 'http://www.example.com:8888';
* $base_url = 'http://www.example.com/drupal';
* $base_url = 'https://www.example.com:8888/drupal';
So better change code to something like (untested):
This also means that we need a new issue to change the examples in default.settings.php for Drupal 8!
Unassigning since I cannot fix this right now, feel free to jump in ;-)
Comment #43
gábor hojtsyShould this be set back again on Drupal 8 because it has the same problem? :/
Comment #44
attiks commentedGabor,
Difficult question, I think we should decide what $base_url is/will be and what values are acceptable. But my experience is limited to the first option ($base_url = 'http://www.example.com';), I never used any of the other options.
I guess we need people using reverse proxies (Varnish), load balancers, ... to give feedback, so we can determine the impact (and see if it's even possible).
Back to 8.x, we either
I think 1 is the sanest to do?
Comment #45
gábor hojtsyYeah, let's fix like #42 in Drupal 8 for now I think.
Comment #46
attiks commentedAccording to KrisBuytaert: "mostly using it for rev proxy setups" (twitter) so in that case we cannot alter the $base_url, AFAIK #42 respects that.
Comment #47
tstoecklerI'm claiming this for the current sprint at DrupalCon.
Comment #48
Carsten Müller commentedadded #42 to drupal 8 core/modules/language/language.negotiation.inc
Comment #50
tstoecklerSo this took a bit longer than expected.
1. The existing test was broken when using dirty URLs
2. We override $options['base_url'] before we do the port checking, but we want to retain a possible port from there.
Comment #51
tstoecklerOops, that change is accidental.
This change is not strictly necessary, but I find the list() syntax much more readable, because the variables, actually have a meaning.
Oops
...
This is not strictly necessary, but I wanted to make it consistent with the call below.
I don't why strcmp() was used here, I don't see a reason not to use assertEqual()
Will roll a followup patch in a moment.
Comment #52
tstoecklerThis one is better, I hope. Bring on those reviews! :-)
Comment #53
tstoecklerOops I only now saw #48. That was a crosspost. Anyway, as point 2. in #50 elaborates, that approach does not work, at least not directly.
Comment #54
tstoecklerAhhh, I uploaded the same files again. I'm sorry. Here we go.
Comment #55
attiks commentedNice work, some minor remarks
isn't it safer to do str_replace(array('http://', 'https://'), '' .... so if gets removed no matter what is in there.
i think it looks cleaner on 1 line
Comment #56
tstoeckler1. I personally don't really know how all the different SSL settings work, I just noticed that this seems to work. Your proposal definitely can't hurt, I'll reroll with that.
2. I did it like that, so we don't need to duplicate the actual URL, but I don't really care either. I'll reroll with that.
Comment #57
tstoecklerHere we go.
Thinking about the $url_scheme / $base_url stuff, I actually think in case the base_url is given, we should take over the scheme, instead of relying on $is_https, just like we take over the port (with this patch).
I'm not sure that that should be part of this patch, though.
Comment #58
Carsten Müller commented$options['base_url'] contains the Protocol (http://, 'https://', ...), so just checking for : is not the best idea ;-)
Comment #59
attiks commentedI shouldn't do that, and certainly not is this patch. The underlying problem is that you can only specify 1 $base_url containing a full URL including scheme and port, which should in a follow-up issue be changed so you only specify the FQDN so it is consistent and works regardless of the scheme and/or port.
FYI: I talked some more to other devops and most of them never use $base_url for Drupal 7 when using a reverse proxy.
Comment #60
Carsten Müller commentedI'm new to this issue and the specific topic itself. So instead of using the base url $_SERVER['SERVER_PORT'] may be better.
But i'm not sure if this port problem should be handled in this issue itself or if it is a more generic problem.
If the port is always given in global $base_url, that could be a workaround to parse the url and get the port.
I can change the patch to parse global $base_url, but my knowledge of the core is not good enough to handle this issue if it should be fixed in a more generic way within Drupal.
Comment #61
attiks commentedI think we should stay with the patch from #57 and handle all the rest in a separate issue.
Re uploading patch from #57
Comment #62
tstoecklerI agree. I definitely there should be a follow-up where we can discuss this in detail. Also because, the whole HTTP/HTTPS thing needs the same discussion.
But this patch fixes an edge-case, but legitimate bug, so let's ship it!
RTBC anyone?
Comment #63
vasi1186 commentedIt also looks good to me. The only thing I've seen is that there is not use of t() function in the tests assertions. In the other parts in core, t() is used, but I am not sure if this is a good thing or not. Anyhow, I think we should follow a common path, either we use t() all the time, or we never use it.
Comment #64
gábor hojtsyt() calls are in progress of being removed from assertion messages. We should not use it for assertion messages.
Comment #65
vasi1186 commentedIn that case, I think this can be RTBC.
Comment #66
catchIf we're saving the original base URL, then we'd be saving the whole thing, not the base URL minus the protocol. We could call it something like 'normalized base url'?
Otherwise I think this is probably fine.
Comment #67
attiks commentednew patch
Comment #69
attiks commented#67: i1645156.patch queued for re-testing.
Comment #71
attiks commented#67: i1645156.patch queued for re-testing.
Comment #72
tstoecklerI'm not sure I am allowed to RTBC this. It looks good, though.
Comment #73
gábor hojtsyResolves the concern from @catch. Back to RTBC.
Comment #74
webchickAwesome, thanks so much for this fix!
Committed and pushed to 8.x. Moving to 7.x for backport.
Comment #75
amonteroFirst attempt for 7.x
I still have to figure out backporting the test changes.
Comment #76
amonteroComment #78
amonteroDisregard previous patch at all, still making my way through language system (= warning, novice at the wheel).
At least this should be modifying the appropiate code. Nothing broke... I'm starting to suspect that I must have something misconfigured :)
Comment #79
attiks commentedNice work, patch is looking good, but we still need the test. If you write the test, can you upload a patch with only the test first so we can see that it will break as you did in #54
Comment #80
gábor hojtsyRemoving from D8MI sprint, where we focus on D8 :)
Comment #81
tstoecklerUnassigning, as I'm not involved with the D7 port.
Comment #82
leschekfm commentedThe patch still applies cleanly against 7.20 and works like a charm.
Comment #82.0
leschekfm commentedUpdated issue summary.
Comment #83
mrharolda commentedWhat happened to this issue? It used to be a D7 release blocker ...
Comment #84
mgifford78: drupal--i1645156-78.D7.patch queued for re-testing.
Comment #85
mgiffordWith more sites moving to using https only, this limitation is becoming more annoying.
The patch in #78 still applies. What do we need to do to get this into D7?
Comment #86
dcam commentedLooks like the tests still need to be backported to D7. See #79.
Comment #87
mgiffordOk, setting it back to Needs work then until we get a patch with those. Thanks @dcam for your work on these issues.
Comment #88
k4v commentedFor me the patch does not work in Drupal 7.34. $options['base_url'] is not set, but i have a $base_url in settings.php.
Comment #89
talhaparacha commentedThe patch from #78 applies cleanly to 7.41. As requested, here is the tests-only patch backported + re-rolled from #54 and #40. Hoping for a quick RTBC for getting the issue fixed in D7 as it has been in D8.
Comment #92
talhaparacha commentedBecause the added tests were supposed to fail until this issue gets fixed...
Comment #93
dcam commentedThank you for your work, @talhaparacha!
A patch which combines the test and the fix must be created now. The issue can't be RTBCed without demonstrating that the fix corrects the test failures.
Edit: nevermind about the missing test failure. It was only reporting a single failure when I wrote that, but now it reports two.
Comment #94
talhaparacha commented@dcam Understood. Here we go; combined patch (fix from #78 and tests from #89) along with the tests-only patch.
Comment #97
talhaparacha commentedComment #98
nicrodgersPatch applies cleanly to 7.41, and manually tested that urls correctly retain the port (if set). Also URLs continue to work correctly if the default port is set and not specified in the URL. #94 demonstrates that the patch (with test) fixes the failing test, so marking this as RTBC.
Comment #99
fabianx commentedNice work! Those i18n patches always have such a nice structure and are well cared for! Thank you!
Marking for D7 commit.
Comment #100
stefan.r commentedCode is essentially the same as D8, backport looks OK to me
Comment #102
fabianx commentedCommitted and pushed to 7.x! Thanks!