-
Bug
-
Resolution: Done
-
Major
-
None
-
None
I think I have identified a bug in the HTTP/2 handling, or in the proxy code - not sure which.
To reproduce it, I modified the io.undertow.examples.http2.HttpServer slightly to force chunked encoding - once I do this, every requests using chrome fails with ERR_SPDY_PROTOCOL_ERROR within chrome when I use SSL - https://localhost:8444
/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.undertow.examples.http2; import static io.undertow.Handlers.predicate; import static io.undertow.Handlers.resource; import static io.undertow.predicate.Predicates.secure; import java.io.InputStream; import java.net.URI; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyStore; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.UndertowOptions; import io.undertow.attribute.ExchangeAttributes; import io.undertow.examples.UndertowExample; import io.undertow.protocols.ssl.UndertowXnioSsl; import io.undertow.server.HttpHandler; import io.undertow.server.HttpServerExchange; import io.undertow.server.handlers.LearningPushHandler; import io.undertow.server.handlers.ResponseCodeHandler; import io.undertow.server.handlers.proxy.LoadBalancingProxyClient; import io.undertow.server.handlers.proxy.ProxyHandler; import io.undertow.server.handlers.resource.PathResourceManager; import io.undertow.server.session.InMemorySessionManager; import io.undertow.server.session.SessionAttachmentHandler; import io.undertow.server.session.SessionCookieConfig; import io.undertow.util.Headers; import io.undertow.util.StatusCodes; import org.xnio.OptionMap; import org.xnio.Xnio; /** * @author Stuart Douglas */ @UndertowExample(value = "HTTP2", location = "https://localhost:8443") public class Http2Server { private static final char[] STORE_PASSWORD = "password".toCharArray(); public static class ForceChunkedHandler implements HttpHandler { private HttpHandler next; public ForceChunkedHandler(HttpHandler next) { this.next = next; } @Override public void handleRequest(HttpServerExchange exchange) throws Exception { exchange.getResponseHeaders().put(Headers.TRANSFER_ENCODING, Headers.CHUNKED.toString()); next.handleRequest(exchange); } } public static void main(final String[] args) throws Exception { String version = System.getProperty("java.version"); System.out.println("Java version " + version); if(version.charAt(0) == '1' && Integer.parseInt(version.charAt(2) + "") < 8 ) { System.out.println("This example requires Java 1.8 or later"); System.out.println("The HTTP2 spec requires certain cyphers that are not present in older JVM's"); System.out.println("See section 9.2.2 of the HTTP2 specification for details"); System.exit(1); } String bindAddress = System.getProperty("bind.address", "localhost"); SSLContext sslContext = createSSLContext(loadKeyStore("server.keystore"), loadKeyStore("server.truststore")); Undertow server = Undertow.builder() .setServerOption(UndertowOptions.ENABLE_HTTP2, false) .addHttpListener(8080, bindAddress) .setHandler(new ForceChunkedHandler(new SessionAttachmentHandler(new LearningPushHandler(100, -1, Handlers.header(resource(new PathResourceManager(Paths.get(System.getProperty("example.directory", System.getProperty("user.home"))), 100)) .setDirectoryListingEnabled(true), "x-undertow-transport", ExchangeAttributes.transportProtocol())), new InMemorySessionManager("test"), new SessionCookieConfig()))).build(); server.start(); SSLContext clientSslContext = createSSLContext(loadKeyStore("client.keystore"), loadKeyStore("client.truststore")); LoadBalancingProxyClient proxy = new LoadBalancingProxyClient() .addHost(new URI("http://localhost:8080")) .setConnectionsPerThread(20); Undertow reverseProxy = Undertow.builder() .setServerOption(UndertowOptions.ENABLE_HTTP2, true) .addHttpListener(8081, bindAddress) .addHttpsListener(8444, bindAddress, sslContext) .setHandler(new ProxyHandler(proxy, 30000, ResponseCodeHandler.HANDLE_404, true, false)) .build(); reverseProxy.start(); } private static KeyStore loadKeyStore(String name) throws Exception { String storeLoc = System.getProperty(name); final InputStream stream; if(storeLoc == null) { stream = Http2Server.class.getResourceAsStream(name); } else { stream = Files.newInputStream(Paths.get(storeLoc)); } try(InputStream is = stream) { KeyStore loadedKeystore = KeyStore.getInstance("JKS"); loadedKeystore.load(is, password(name)); return loadedKeystore; } } static char[] password(String name) { String pw = System.getProperty(name + ".password"); return pw != null ? pw.toCharArray() : STORE_PASSWORD; } private static SSLContext createSSLContext(final KeyStore keyStore, final KeyStore trustStore) throws Exception { KeyManager[] keyManagers; KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, password("key")); keyManagers = keyManagerFactory.getKeyManagers(); TrustManager[] trustManagers; TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); trustManagers = trustManagerFactory.getTrustManagers(); SSLContext sslContext; sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, trustManagers, null); return sslContext; } }
If I change this line: .setServerOption(UndertowOptions.ENABLE_HTTP2, true) from true to false, then it works - thats why I am not sure if the issue is in the proxy or in the HTTP2 handling.
EDIT: Just managed to try it out with MSIE - and there it works, so it definately seems to be chrome related - might it be a bug in chrome ?