Problem/Motivation

I am working on a client project where the following are true:

  • Drupal Commerce is being used to facilitate checking out of products.
  • The site is multilingual and Language Negotiation is being set "by domain name" instead "by path" as available here /admin/config/regional/language/configure.
  • A different root level domain is being used for each language.
  • Securepages is being used to ensure desired behaviour between HTTP and HTTPS pages, and /cart is one of the pages that is being forced to HTTPS.
  • "Switch back to http pages when there are no matches" has been checked off within Securepages settings.

In my particular scenario, my client has domain-english.com associated with the English language and domain-french.com associated with the French language. There are no other languages on the site. We have elected to use "by domain name" as the language negotiation method because we aren't using the domain.com/en VS domain.com/fr paradigm for showing content in different languages.

For performance reasons, we have "Switch back to http pages when there are no matches" checked off within the Securepages settings (/admin/config/system/securepages). This is because if a user lands themselves on one of the pages that should be served over HTTPS, the remainder of their visit to the site could very well remain on HTTPS which would thwart our ability to use Varnish cache for anonymous HTTP traffic. We want the user back on HTTP if they are visiting a page that does not have an explicit rule to be delivered over HTTPS.

Under these conditions, when a user adds something to their cart and then elects to click the "Checkout" button, the user is redirected to a Drupal Search page with "ttps%3A//domain-english.com/cart" as a search term. This is because upon viewing the source of that form, we can see the form action has been erroneously rewritten to something like this: http://domain-english.com/ttps%3A//domain-english.com/cart. This is not a valid form URL, so Drupal does the only thing it knows to do: process it as a Drupal search query.

This means any attempt to get further in the checkout process is impossible unless you edit the source directly before proceeding.

Proposed resolution

There is no work around that satisfies all requirements. You can either elect to use path based negotiation instead of domain name, or you can uncheck "Switch back to http pages when there are no matches". Doing so might have performance implications depending on your architecture, or may not be so big a deal.

I have authored a patch to the hook_form_alter() implementation within securepages.module that adds a logical check for the base path length. In the event that all conditions above are satisfied, the base_path is actually interpreted as "/" initially and then substr() is erroneously interpreting the content and re-writing it improperly.

Remaining tasks

  1. The patch is ready for review.
  2. Any required Simpletest adjustments that the community/maintainer feels necessary. I regret that I am not aware enough of how to write Simpletests that I can do it myself with my available time. With the patch in place, I have manually tested all permutations of these settings to ensure the patch doesn't introduce any unexpected implications when some or all of the conditions are not present.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

timfernihough’s picture

james.williams’s picture

The patch above will not work for absolute URLs. #2029965: Properly handle a form action url that does not begin with the base path is a very similar issue, does the patch there (which does handle absolute URLs) solve your issue too?

xaris.tsimpouris’s picture

Indeed, patch from #1 did not work. However I made the following changes (hard to create a patch at the moment) and it seems that it works. Within function "securepages_form_alter"

Line #77: global $is_https, $user, $language;
..gets: global $is_https, $user, $language, $base_url;

and Line #85: $url = substr(rawurldecode($form['#action']), strlen(base_path()));
..gets: $url = str_replace($base_url . '/', '', rawurldecode($form['#action']));

Form actions seem to work as expected now.