All,
On 10/7/24 16:52, Christopher Schultz wrote:
All,
I thought it might be "fun" to play around with 103 Early Hints, which
is a feature with support added in servlet.next which means Tomcat 12.
It can be used currently with Tomcat 9.0 and later, as long as you are
willing to downcast the HttpServletResponse to a Tomcat-private class
and you can make a call to Response.sendEarlyHints.
It occurs to me that we (Tomcat) could make it easier for applications
to use this feature by providing something like a Filter that would
use Tomcat internal libraries, but insulate the application from such
ugliness.
I have two ideas for how this would be implemented:
1. A new Filter dedicated to (a) setting Link headers and (b) calling
sendEarlyHints
2. As an option for the RewriteValve value, e.g. as a flag
I wrote a quick implementation of (1) above and it occurred to me that
is was pretty trivial, so I came up with idea (2) above as maybe a way
to add a feature to an existing component rather than building a new one.
When I started looking at the changes required for (2), it seems to me
that the only thing that made any sense would be to do the following:
RewriteRule .*\.jsp $0 [SEH]
The "SEH" is a proposed "Send Early Hints" flag which would invoke the
response.sendEarlyHints method which .... sends a 103 Early Hints
response and then proceeds with the rewrite/request.
I've decided that I don't really like (2) because you have to
configure a useless rewrite operation just to add the SEH flag. Also,
RewriteValve doesn't help you with the Link headers so you are back to
writing a Filter and then arranging to have that Filter run *before*
RewriteValve which ... could be a problem for you.
So I think I'm going to pursue option (1) above, looking at doing
something like I have below. Comments welcome and encouraged.
I build a practical example to test this with my own application, and I
found that the following implementation contains everything I needed to
use 103 Early Hints in a practical way.
Example configuration:
=== CUT ===
<filter>
<description>
Configures the application to send Early Hints to load resources
such as CSS and scripts.
</description>
<filter-name>early-hints</filter-name>
<filter-class>org.apache.catalina.filters.EarlyHintsFilter</filter-class>
<init-param>
<param-name>csp.a</param-name>
<param-value>default-src:self;</param-value>
</init-param>
<init-param>
<param-name>link.a</param-name>
<param-value><${contextPath}/css/site.css>; rel=preload;
as=style</param-value>
</init-param>
<init-param>
<param-name>link.b</param-name>
<param-value><${contextPath}/js/scripts.js>; rel=preload;
as=style</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>early-hints</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
=== CUT ===
package org.apache.catalina.filters;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.connector.ResponseFacade;
/**
* A Filter that adds a series of
*/
public class EarlyHintsFilter
implements Filter
{
private final ArrayList<String> csps = new ArrayList<String>(1);
private final ArrayList<String> hints = new ArrayList<String>();
@Override
public void init(FilterConfig config) throws ServletException {
Enumeration<String> paramNames = config.getInitParameterNames();
while(paramNames.hasMoreElements()) {
String name = paramNames.nextElement();
if(name.startsWith("csp.")) {
csps.add(config.getInitParameter(name));
} else if(name.startsWith("link.")) {
String hint = config.getInitParameter(name);
int pos = hint.indexOf("${contextPath}");
if(pos >= 0) {
hint = hint.replace("${contextPath}",
config.getServletContext().getContextPath());
}
hints.add(hint);
} else {
config.getServletContext().log("WARNING: Unexpected
init-param to EarlyHintsFilter: " + name);
}
}
}
@Override
public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse rsp = (HttpServletResponse)response;
if(!csps.isEmpty()) {
for(String csp : csps) {
rsp.addHeader("Content-Security-Policy", csp);
}
}
if(!hints.isEmpty()) {
for(String hint : hints) {
rsp.addHeader("Link", hint);
}
((ResponseFacade)rsp).sendEarlyHints();
}
chain.doFilter(request, response);
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org