Component choosing to proxy a response

I have a page component that most of the time adds attribute(s) to the hstRequest and passes on to the freemarker templates for rendering.

However, in some cases, I need the component to proxy an entire response and send the other response instead (for the entire page). Instead, i get the proxied response inlined in the middle of my page. How should I solve this cleanly?

public class MyComponent extends BaseHstComponent {
    @Override
    public void doBeforeRender(HstRequest hstRequest, HstResponse hstResponse) throws HstComponentException {
        MyData data = dao.fetch(hstRequest.getPathInfo());
        if (shouldProxy(data) ) {
            // proper steps
        }
    }
}

Do I need to forward the request to a different sitemapitem or even a proxyservlet (such as https://github.com/mitre/HTTP-Proxy-Servlet or Netflix Zuul https://spring.io/guides/gs/routing-and-filtering/? I don’t want to build a filter because the filter would do the same work as the component to fetch the data and determine if a proxy is required.

First, configure the reverse-proxy servlet [1] independently from HST things. For example, /site/rproxy/** should be handled by the reverse-proxy servlet. i.e, /site/rproxy/example/a/b/c.html → http://www.example.com/a/b/c.html.
Second, you should be able to build the proxy request path (e.g, “/rproxy/a/b/c.html”) in your component.
Third, change the renderPath dynamically in your #doBeforeRender(). e.g, hstResponse.setRenderPath("/rproxy/a/b/c.html").

Then it should do the trick.

Woonsan

[1] ServletProxy - Apache Tomcat - Apache Software Foundation
The fourth option, “Apache Portals WebContent-2 Reverse Proxy Module”, is really flexible.

I followed the instructions on http://portals.apache.org/applications/webcontent2/reverse-proxy-module.html and I get “ResolvedSiteMapItemImpl cannot be created correctly” in STDOUT, 404/“Not Found” on the page.

site/pom.xml:

<dependency>
    <!-- https://portals.apache.org/applications/webcontent2/reverse-proxy-module.html -->
    <groupId>org.apache.portals.applications</groupId>
    <artifactId>apa-webcontent2-reverse-proxy</artifactId>
    <version>2.0</version>
</dependency>

site WEB-INF/web.xml:

<servlet>
    <servlet-name>ReverseProxyServlet</servlet-name>
    <servlet-class>org.apache.portals.applications.webcontent2.proxy.servlet.SimpleReverseProxyServlet</servlet-class>
    <init-param>
        <param-name>mappings</param-name>
        <param-value>/WEB-INF/rproxy-mappings.yaml</param-value>
    </init-param>
    <init-param>
        <param-name>ssl-hostname-verifier</param-name>
        <param-value>ALLOW_ALL_HOSTNAME_VERIFIER</param-value>
    </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>ReverseProxyServlet</servlet-name>
  <url-pattern>/reverse-proxyservlet/*</url-pattern>
</servlet-mapping>

site WEB-INF/rproxy-mappings.yaml:

--- !simple
local: /portals/applications/
remote: http://portals.apache.org/applications/
contentRewriters:
    text/html: !!org.apache.portals.applications.webcontent2.proxy.rewriter.DefaultReverseProxyTextLineContentRewriter []

But I get a 404/Not Found when I browse to http://localhost:8080/site/reverse-proxyservlet/portals/applications/webcontent2/index.html (I would expect it to render the same as http://portals.apache.org/applications/webcontent2/index.html)

STDOUT shows:

[INFO] [talledLocalContainer] 06.02.2019 15:58:54 WARN  http-nio-8080-exec-6 [ResolvedSiteMapItemImpl.resolveComponentConfiguration:191] ResolvedSiteMapItemImpl cannot be created correctly, because the component configuration id hst:pages/pagenotfound cannot be found.

Am I integrating these wrong?

That’s because HstFilter is still taking the control when you make a request at /reverse-proxyservlet/…
As the request path should not be handled by HstFilter like normal HST sitemap items, and the reverse proxy servlet has nothing to do with HST-2, you should configure the path to be bypassed by HstFilter:

  • copy /hst:hst/hst:configurations/hst:default/hst:sitemap/binaries to /hst:hst/hst:configurations/hst:default/hst:sitemap/reverse-proxyservlet (including children).
  • remove hst:refId.

Then the reverse proxy servlet will work independently.

Woonsan

Thanks! That resolved the proxy issue; now I can proceed with forwarding to it.

I have the proxy solution working. And I have the component setting the renderPath - but that only inlines the (proxied) response in the middle of the page via hstResponse.setRenderPath(proxyPath)

Is there a way to not include the header/footer of the page? I want the response to ONLY have the output of the proxy, kind of like how a siteMapItemHandler can change the resolvedSiteMapItem.

Normally, we would handle this in a SiteMapItemHandler to change the resolvedSiteMapItem - but I wouldn’t know if I want to change the siteMapItem until the component has called an API (to determine if I proxy or not). If I don’t proxy, the API call returns an object that the component reacts to.

I guess my real question should be:

  • can my siteMapItemHandler call the API and “pass” the resulting object to the component somehow (if so, how)
  • OR can my component change the resolvedSiteMapItem kind of after the fact
  • => I really don’t want to call the same API twice.

By “the API”, do you mean the backend web service you wanted to do reverse-proxying in an HstComponent?
If so, what does the response look like? JSON, XML or HTML?

The pieces are:

  • JSON API returns JSON if 200 response, but 204, 404 response codes have no JSON and are significant
  • HTML resources to proxy in certain cases
  • SiteMapItemHandler (currently just looks at the hstRequest.getPathInfo() to determine if it meets certain cases)
  • RenderComponent that calls the API and handles at least 3 cases.

Current processing ends up inlining the proxied response in middle of the page. Want the proxied HTML resource to BE the response if used.

My gut is telling me that the SiteMapItemHandler should call the JSON API. Depending on the response code, it should:

  1. 200 => Deserialize JSON and “pass” object to the Component
  2. 204 => proxy by resolving to new ResolvedSiteMapItemImpl with proxy path (that maps to external HTML)
  3. 404 => resolve to a “not found” siteMapItem
  4. ??? => there might also be an “expired” case

I want to avoid having both the siteMapItemHandler, Component both calling the same API with the same data.