HST Custom https support
Custom Domain Specific HTTPS Support
When you need https/ http support only on site (mount) / sitemap item level, you only need HST seamless https support. However, the HST https support on mount or sitemap item level might be too coarse grained for your use case. For example your use case might be one of these below:
- Any document that contains a form compound should be visited over https
- Any document that contains a link to a form document should be visited over https
- Any document that contains the mixin myproject:secure must be visited over https
If all other documents should be visited over http, the above use cases cannot be fulfilled with the seamless https support. This is because on any wildcard sitemap item, you can have some documents that should be visited over http, and some over https. These use cases are domain specific ones that the HST cannot support out of the box. For this, you have to develop your own domain specific logic. The HST however does provide a base utility for you that makes it very easy.
Prerequisite
To support custom https support, even in a HstComponent java class, you need to set on the host (or one of its ancestor) that is meant to support custom https support the following property:
hst:customhttpssupport: true
If you find your application resulting in a browser redirect loop, most like you forgot to set the property above on one of the hst:virtualhost nodes. The property makes hst:customhttpssupport assures that the custom domain specific https support does not conflict with the seamless HST https support: What namely could happen, is that the seamless HST https support indicates that some request must be over http, while at the same time, the domain specific custom https support indicates the request should be over https. Without hst:customhttpssupport = true, this results in a browser redirect loop. Marking a host as customhttpssupport means as much as : Even if the (default) hst:scheme is http, a request over https is always allowed.
Custom Java Code
You can write a redirect based on some criteria easily in a HstComponent java class in the doBeforeRender with HstResponse#sendRedirect(...), or easier, with HstResponseUtils#sendRedirect(...). However, it is not trivial to get it right in all circumstances. For example, during development you most likely don't want the redirect to https, on hosts on production for which you do not have an SSL certificate you might want to skip the redirect (hosts with hst:schemeagnostic = true), for requests in the CMS channel manager you don't want redirects as the site URLs already piggyback on the scheme of the cms host, etc. Taking into account all these corner cases as a developer is a tedious job. And, if you would cover all these cases, there is still the disadvantage that before the HstComponent is hit that does the redirect, already a lot of other components have had their doBeforeRender called : In other words, it is also inefficient.
To make it easy for domain specific https support that covers all the corner cases out-of-the-box already, the HST ships with an AbstractHttpsSchemeValve, that contains one abstract method to be implemented by developers:
public abstract boolean requiresHttps(ValveContext context);
Even if for some request over http this method would return true, the HST might not redirect to https. This is because of the corner cases mentioned above that are already covered by AbstractHttpsSchemeValve#invoke(ValveContext). If the HST knows in advance that a redirect to https cannot be honored, the AbstractHttpsSchemeValve does not even invoke requiresHttps.
Example Valve
Assume you have in your project a jcr mixin, say myproject:secure, on documents that should be served over https. You can then write your valve implementation as follows:
public class HttpsSchemeValve extends AbstractHttpsSchemeValve { @Override public boolean requiresHttps(final ValveContext context) { final HippoBean contentBean = context.getRequestContext() .getContentBean(); if (contentBean == null ) { return false; } try { return contentBean.getNode().isNodeType("myproject:secure"); } catch (RepositoryException e) { throw new RuntimeRepositoryException(e); } } }
Inject your custom valve into an existing pipeline
Through hst-assembly.overrides spring configuration, you can inject your custom scheme valve into an existing hst pipeline. Although it is allowed to inject the valve also in other places than shown below, the preferred location is as below. At least it is required to be located after the initializationValve and before the aggregationValve. Thus, for example at
+ site + src + main + resources + META-INF + hst-assembly + overrides
add httpsScheme-valve.xml containing:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="httpsSchemeValve" class="com.example.hst.container.HttpsSchemeValve"> <property name="valveName" value="httpsSchemeExampleValve" /> <property name="afterValves" value="initializationValve"/> <property name="beforeValves" value="cmsSecurityValve"/> </bean> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject"> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="org.hippoecm.hst.core.container.Pipelines" /> <property name="targetMethod" value="getPipeline"/> <property name="arguments"> <value>DefaultSitePipeline</value> </property> </bean> </property> <property name="targetMethod" value="addInitializationValve"/> <property name="arguments"> <ref bean="httpsSchemeValve" /> </property> </bean> </beans>
The spring configuration above will inject your custom HttpsSchemeValve into the DefaultSitePipeline right after the initializationValve.
Working example
Start up the testsuite project at https://code.onehippo.org/cms-community/hippo-testsuite/tags/hippo-testsuite-3.0.0 and follow the steps below to test it.
Make sure you have configured Apache HTTP Server properly.
You may need to add to your hosts file (Ubuntu /etc/hosts) something like the following:
127.0.0.1 cms.example.com 127.0.0.1 www.example.com
After starting the testsuite:
1) Go to /console and go to http://localhost:8080/cms/console/?path=/content/documents/demosite/common/about-us.
2) To that document add the mixin 'testsuite:secure'
Make sure that you add the www.example.com host as follows:
/hst:hst: /hst:hosts: /example-env: hst:cmslocation: http://cms.example.com hst:defaultport: 80 /com: hst:showcontextpath: false hst:showport: false /example: /www: /hst:root: hst:mountpoint: /hst:hst/hst:sites/myhippoproject
Now, the following scenarios should be as follows:
1) when visiting http://localhost:8080/site/about there should be *NO* redirect to https:
- reason : localhost at http://localhost:8080/cms/console/?path=/hst:hst/hst:hosts/dev-localhost/localhost has hst:schemeagnostic = true
2) when visiting http://www.example.com/about you should get a redirect loop
- reason : The custom testsuite HttpsSchemeExampleValve tries to redirect to https, but the HST Container after the redirect redirects to http because the sitemap item scheme is http
3) At http://localhost:8080/cms/console/?path=/hst:hst/hst:hosts/example-env/com set : hst:customhttpssupport = true. visit http://www.example.com/about. You should get a redirect to https://www.example.com/about.
- reason : This now works because hst:customhttpssupport = true indicates that https requests should pass even if sitemap item indicates http
4) Confirm that when on https://www.example.com/about, all other menu links are fully qualified links starting http://;
5) Confirm that when on https://www.example.com/about, all resources (css, js) are also fetched over https.
6) The channel manager should work regardless localhost has hst:schemeagnostic = true for both localhost as cms.example.com (you might have to switch hst:cmslocation for testing localhost)