Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [jetty-users] embedded welcomefiles+http2-push+gzip-static-content+rest-api configuration

Joakim I am _so_ appreciative you gave this a look and reiterated my fumbling attempts.  thank you so much!  I'd like to gain the familiarity you are demonstrating, but chances are if it works I might never revisit this again!

perhaps you might make a small jetty docs page for PWA and AMP hosting that puts emphasis on the site performance metrics along these lines.  the sample materials I have pulled from thus far have spanned early jetty 8.x thru now to cover the breadth of h2/push/gzip/api/static. 

Thanks!

On Mon, Feb 18, 2019 at 10:23 PM Joakim Erdfelt <joakim@xxxxxxxxxxx> wrote:
There's a few shortcuts you are taking that are not helping you.
  • HttpConfiguration isn't 100% common between connectors, use the hierarchy.
  • ServerRequestCustomizer only belongs on secure connector
  • SecureScheme / SecurePort configuration belongs on non-secure connector
  • Constraints / ConstraintMappings are not needed if you use the SecureRedirectHandler
  • You shouldn't need to wrap handlers as much as you are doing.
  • ResourceHandler is a poor choice for within a ServletContextHandler (it's designed to be used with DefaultServlet, not a ResourceHandler)
  • ServletContextHandler should have a BaseResource setup, many things use it. (various Servlet APIs and also the DefaultServlet)
  • A BaseResource can be directly created from a java Path, use a PathResource
etc ...

Here's a broken down version of what you are attempting to do, with proper initialization and configuration.

        Server server = new Server();

        // HTTP Port

        HttpConfiguration httpConfig = new HttpConfiguration();
        httpConfig.setSecureScheme("https"); // needed on non-ssl connector for secure redirect
        httpConfig.setSecurePort(SSL_PORT); // needed on non-ssl connector for secure redirect
        httpConfig.setSendXPoweredBy(false);
        httpConfig.setSendServerVersion(false);

        HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory();
        ServerConnector httpConnector = new ServerConnector(server, httpConnectionFactory);
        httpConnector.setPort(8080);
        server.addConnector(httpConnector);

        // TLS (SSL) Port

        HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); // extends from non-ssl
        httpsConfig.addCustomizer(new SecureRequestCustomizer()); // only valid on SSL/TLS connector

        HTTP2ServerConnectionFactory http2ConnectionFactory = new HTTP2ServerConnectionFactory(httpsConfig);
        ALPNServerConnectionFactory alpnConnectionFactory = new ALPNServerConnectionFactory();
        alpnConnectionFactory.setDefaultProtocol(httpConnectionFactory.getProtocol());
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath(GzipHttp2PushFilterExample.class.getResource("/keystore").toExternalForm());
        sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
        sslContextFactory.setKeyStorePassword("changeit");
        sslContextFactory.setUseCipherSuitesOrder(true);
        SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, alpnConnectionFactory.getProtocol());
        ServerConnector httpsConnector = new ServerConnector(server, sslConnectionFactory, http2ConnectionFactory,  httpConnectionFactory);

        server.addConnector(httpsConnector);

        // Non Handler Configuration
        server.setRequestLog(new AsyncNCSARequestLog());

        // Servlet ROOT Context
        ServletContextHandler context = new ServletContextHandler();
        context.setContextPath("/");
        context.setWelcomeFiles(new String[]{"index.html"});
        context.setBaseResource(new PathResource(Paths.get("some","path", "somewhere")));

        // Push Cache Filter
        FilterHolder pushFilter = context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
        pushFilter.setInitParameter("maxAssociations", "" + 32);
        pushFilter.setInitParameter("ports", "" + SSL_PORT);

        // Your servlet
        context.addServlet(AdapterServlet.class, "/*"); // won't serve static files if you use this url-pattern!

        // what serves static files from a ServletContext, always added last
        ServletHolder defHolder = new ServletHolder("default", DefaultServlet.class); // always named "default"
        defHolder.setInitParameter("dirAllowed", "false");
        context.addServlet(defHolder, "/"); // always at url-pattern "/"

        // Handler Tree
        HandlerList handlers = new HandlerList();
        handlers.addHandler(new SecuredRedirectHandler()); // no need for Constraints / Constraint Mappings
        handlers.addHandler(new GzipHandler()); // wrapping / nesting is optional
        handlers.addHandler(context);
        handlers.addHandler(new DefaultHandler()); // always last in handler tree, to help with errors and configuration mistakes

        server.setHandler(handlers);


Joakim Erdfelt / joakim@xxxxxxxxxxx


On Mon, Feb 18, 2019 at 9:38 AM James Northrup <jim@xxxxxxxxxxx> wrote:
hi Simone, 

just checking in.

i just wanted to pose a question:
the java code at the bottom of this email is somewhat hard to follow.  is this the most succinct programatic way to create the desired h2 server, static, api, and gzip ?

this still fails with pushcachefilter, as well highlighted in the previous gist..  

package id.kbr.prime;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlets.PushCacheFilter;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.servlet.FilterRegistration;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.TreeMap;
import static id.kbr.prime.JettyStarterKt.SSL_PORT;
import static org.apache.commons.lang3.ArrayUtils.toArray;
public class JettyJavaStarter implements AutoCloseable {
private final Server server;
public JettyJavaStarter(String... args) {
server = new Server() {
{
HttpConnectionFactory httpFactory = new HttpConnectionFactory(new HttpConfiguration() {{
addCustomizer(new SecureRequestCustomizer());
}});
HTTP2ServerConnectionFactory http2factory = new HTTP2ServerConnectionFactory(new HttpConfiguration() {{
setSecureScheme("https");
setSecurePort(SSL_PORT);
setSendXPoweredBy(false);
setSendServerVersion(false);
addCustomizer(new SecureRequestCustomizer());
}});
ALPNServerConnectionFactory alpn1 = new ALPNServerConnectionFactory() {{
setDefaultProtocol(httpFactory.getProtocol());
}};
setRequestLog(new AsyncNCSARequestLog());
setConnectors(
new Connector[]{
new ServerConnector(this, httpFactory) {{
setPort(8080);
}},
new ServerConnector(this, new SslConnectionFactory(new SslContextFactory() {{
setKeyStorePath(JettyStarter.class.getResource("/keystore").toExternalForm());
setCipherComparator(HTTP2Cipher.COMPARATOR);
setKeyStorePassword("changeit");
setUseCipherSuitesOrder(true);
}}, alpn1.getProtocol()), http2factory, httpFactory) {{
setPort(SSL_PORT);
}}
}
);
setHandler(new ConstraintSecurityHandler() {{
addConstraintMapping(new ConstraintMapping() {{
setPathSpec("/*");
setConstraint(new Constraint() {{
setDataConstraint(DC_CONFIDENTIAL);
}});
setHandler(new GzipHandler() {{
setHandler(new ContextHandlerCollection(new ServletContextHandler() {{
setContextPath("/");
setHandler(new ResourceHandler() {{
setWelcomeFiles(toArray("index.html"));
setBaseResource(Resource.newResource(Paths.get(args.length > 0 ? args[0] : AdapterServlet.Companion.getResourceBase().toString())));
}});
//the main feature: pass or fail?
FilterRegistration.Dynamic push = getServletContext().addFilter("push", PushCacheFilter.class);
push.setInitParameters(new HashMap<String, String>() {{
put("maxAssociations", "" + 32);
put("ports", "" + SSL_PORT);
}});
}},
new ServletContextHandler() {{
addServlet(AdapterServlet.class, "/*");
}}
));
}});
}});
}});
}
};
}
public void start() throws Exception {
server.start();
}
@Override
public void close() throws Exception {
server.stop();
}
public static void main(String... args) throws Exception {
AppKt.hz = Hazelcast.getOrCreateHazelcastInstance(new Config(args.length > 1 ? args[1] : "default"));
JettyJavaStarter it = new JettyJavaStarter(args);
it.start();
synchronized (it) {
it.wait();
}
}
}

On Sun, Feb 17, 2019 at 1:28 PM James Northrup <jim@xxxxxxxxxxx> wrote:

at id.kbr.prime.JettyJavaStarter.main(JettyJavaStarter.java:111)
Suppressed: java.lang.NullPointerException
at org.eclipse.jetty.servlets.PushCacheFilter.init(PushCacheFilter.java:114)
at org.eclipse.jetty.servlet.FilterHolder.initialize(FilterHolder.java:136)


again, whatever I'm doing is just uninformed guesswork from incomplete knowledge.  the reason for various features is to support 100/100 lighthouse score on hnpwa or thereabouts.

roughly speaking, jetty should be able to host e.g. https://github.com/dsolimando/hnpwa-mobileelements  with http2-push as designed.  adding an additional rest-api is the tweak you need to be able to drive the static content with java backend frameworks .





On Sun, Feb 17, 2019 at 11:54 AM James Northrup <jim@xxxxxxxxxxx> wrote:

On Fri, Feb 15, 2019 at 2:59 PM Simone Bordet <sbordet@xxxxxxxxxxx> wrote:
Hi,


On Fri, Feb 15, 2019 at 6:29 AM James Northrup <jim@xxxxxxxxxxx> wrote:
>
> revising the servletcontextholder changes the result:   https://gist.github.com/jnorthrup/792a43d73178ee38d686035fca3d146d

It NPEs at PushCacheFilter.java:114, which is:

config.getServletContext().setAttribute(config.getFilterName(), this);

A NPE there is not possible, unless "config" is null, or the
ServletContext is null.
In both cases it is a major break of the implementation of the Servlet
specification, which I doubt it's the case (we would have a million
reports by now).

Have you tried to _not_ use coroutines, at least to initialize Jetty?

I would recommend that you start with the simplest possible Jetty
server configuration: a server, one connector, one
ServletContextHandler.
Try that and see if it works. Then start adding things until it breaks.

Since it's Kotlin, try also to write it in pure Java first and then
convert to Kotlin.

--
Simone Bordet
----
http://cometd.org
http://webtide.com
Developer advice, training, services and support
from the Jetty & CometD experts.
_______________________________________________
jetty-users mailing list
jetty-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/jetty-users
_______________________________________________
jetty-users mailing list
jetty-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/jetty-users
_______________________________________________
jetty-users mailing list
jetty-users@xxxxxxxxxxx
To change your delivery options, retrieve your password, or unsubscribe from this list, visit
https://www.eclipse.org/mailman/listinfo/jetty-users

Back to the top