From a62f68e438df92157344dc0a53b4d6ef2e9d4f8e Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 14 Nov 2014 09:46:46 +1100 Subject: [PATCH 1/4] Added 421 MISDIRECTED_REQUEST --- .../src/main/java/org/eclipse/jetty/http/HttpStatus.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java index 70d8c3df04d..2531b4a1025 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpStatus.java @@ -665,6 +665,7 @@ public class HttpStatus public final static int UNSUPPORTED_MEDIA_TYPE_415 = 415; public final static int REQUESTED_RANGE_NOT_SATISFIABLE_416 = 416; public final static int EXPECTATION_FAILED_417 = 417; + public final static int MISDIRECTED_REQUEST_421 = 421; public final static int UNPROCESSABLE_ENTITY_422 = 422; public final static int LOCKED_423 = 423; public final static int FAILED_DEPENDENCY_424 = 424; @@ -802,6 +803,8 @@ public class HttpStatus REQUESTED_RANGE_NOT_SATISFIABLE(REQUESTED_RANGE_NOT_SATISFIABLE_416, "Requested Range Not Satisfiable"), /** 417 Expectation Failed */ EXPECTATION_FAILED(EXPECTATION_FAILED_417, "Expectation Failed"), + /** 421 Misdirected Request(RFC7234)y */ + MISDIRECTED_REQUEST(MISDIRECTED_REQUEST_421, "Misdirected Request"), /** 422 Unprocessable Entity */ UNPROCESSABLE_ENTITY(UNPROCESSABLE_ENTITY_422, "Unprocessable Entity"), /** 423 Locked */ From 593cb390591a937af401af379ccd9424ecaa6591 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 14 Nov 2014 10:28:04 +1100 Subject: [PATCH 2/4] Implemented the PROXY protocol Removed old HttpParser PROXY tests --- .../eclipse/jetty/http/HttpParserTest.java | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java index 342cec10f40..d159883cf93 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java @@ -1496,63 +1496,6 @@ public class HttpParserTest } - @Test - public void testProxyProtocol() throws Exception - { - ByteBuffer buffer=BufferUtil - .toBuffer("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80\015\012" - +"GET / HTTP/1.1\015\012" - +"Host: localhost \015\012" - +"Connection: close\015\012"+"\015\012"+"\015\012"); - - Handler handler=new Handler(); - HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler); - parseAll(parser, buffer); - - assertTrue(_headerCompleted); - assertTrue(_messageCompleted); - assertEquals("GET", _methodOrVersion); - assertEquals("/", _uriOrStatus); - assertEquals("HTTP/1.1", _versionOrReason); - assertEquals("PROXY TCP4 107.47.45.254 10.0.1.116 27689 80", handler._proxy); - assertEquals("Host", _hdr[0]); - assertEquals("localhost", _val[0]); - assertEquals("Connection", _hdr[1]); - assertEquals("close", _val[1]); - assertEquals(1, _headers); - } - - @Test - public void testSplitProxyHeaderParseTest() throws Exception - { - Handler handler=new Handler(); - HttpParser parser=new HttpParser((HttpParser.RequestHandler)handler); - - ByteBuffer buffer=BufferUtil.toBuffer("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80\015\012"); - parser.parseNext(buffer); - - buffer=BufferUtil.toBuffer( - "GET / HTTP/1.1\015\012" - +"Host: localhost \015\012" - +"Connection: close\015\012" - +"\015\012" - +"\015\012"); - - parser.parseNext(buffer); - assertTrue(_headerCompleted); - assertTrue(_messageCompleted); - assertEquals("GET", _methodOrVersion); - assertEquals("/", _uriOrStatus); - assertEquals("HTTP/1.1", _versionOrReason); - assertEquals("PROXY TCP4 207.47.45.254 10.0.1.116 27689 80", handler._proxy); - assertEquals("Host", _hdr[0]); - assertEquals("localhost", _val[0]); - assertEquals("Connection", _hdr[1]); - assertEquals("close", _val[1]); - assertEquals(1, _headers); - } - - @Test public void testFolded() throws Exception { From 5b60e0c98d5aa0643ad0684c42d9a09cc9827b6e Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 14 Nov 2014 10:53:37 +1100 Subject: [PATCH 3/4] support h2-15 and h2-14 --- .../AbstractHTTP2ServerConnectionFactory.java | 2 +- .../server/AbstractConnectionFactory.java | 20 ++++++++- .../jetty/server/AbstractConnector.java | 29 ++++++++++--- .../jetty/server/ConnectionFactory.java | 10 ++++- .../NegotiatingServerConnectionFactory.java | 43 ++++++++++--------- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java index 9ffff48309e..9ca02220265 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/AbstractHTTP2ServerConnectionFactory.java @@ -43,7 +43,7 @@ public abstract class AbstractHTTP2ServerConnectionFactory extends AbstractConne public AbstractHTTP2ServerConnectionFactory() { - super("h2-14"); + super("h2-15","h2-14"); } public boolean isDispatchIO() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java index 96fb3a2d81e..1081fbb6618 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnectionFactory.java @@ -18,6 +18,10 @@ package org.eclipse.jetty.server; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -28,11 +32,19 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; public abstract class AbstractConnectionFactory extends ContainerLifeCycle implements ConnectionFactory { private final String _protocol; + private final List _protocols; private int _inputbufferSize = 8192; protected AbstractConnectionFactory(String protocol) { _protocol=protocol; + _protocols=Collections.unmodifiableList(Arrays.asList(new String[]{protocol})); + } + + protected AbstractConnectionFactory(String... protocols) + { + _protocol=protocols[0]; + _protocols=Collections.unmodifiableList(Arrays.asList(protocols)); } @Override @@ -41,6 +53,12 @@ public abstract class AbstractConnectionFactory extends ContainerLifeCycle imple return _protocol; } + @Override + public List getProtocols() + { + return _protocols; + } + public int getInputBufferSize() { return _inputbufferSize; @@ -67,7 +85,7 @@ public abstract class AbstractConnectionFactory extends ContainerLifeCycle imple @Override public String toString() { - return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol()); + return String.format("%s@%x%s",this.getClass().getSimpleName(),hashCode(),getProtocols()); } public static ConnectionFactory[] getFactories(SslContextFactory sslContextFactory, ConnectionFactory... factories) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java index bdfe3474b89..6bd4e30d43d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AbstractConnector.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -358,17 +359,33 @@ public abstract class AbstractConnector extends ContainerLifeCycle implements Co { synchronized (_factories) { - String key=StringUtil.asciiToLowerCase(factory.getProtocol()); - ConnectionFactory old=_factories.remove(key); - if (old!=null) + Set to_remove = new HashSet(); + for (String key:factory.getProtocols()) + { + key=StringUtil.asciiToLowerCase(key); + ConnectionFactory old=_factories.remove(key); + if (old!=null) + { + if (old.getProtocol().equals(_defaultProtocol)) + _defaultProtocol=null; + to_remove.add(old); + } + _factories.put(key, factory); + } + + // keep factories still referenced + for (ConnectionFactory f : _factories.values()) + to_remove.remove(f); + + // remove old factories + for (ConnectionFactory old: to_remove) { - if (old.getProtocol().equals(_defaultProtocol)) - _defaultProtocol=null; removeBean(old); if (LOG.isDebugEnabled()) LOG.debug("{} removed {}", this, old); } - _factories.put(key, factory); + + // add new Bean addBean(factory); if (_defaultProtocol==null) _defaultProtocol=factory.getProtocol(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java index e23739d8993..c2ec8899b70 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ConnectionFactory.java @@ -19,6 +19,8 @@ package org.eclipse.jetty.server; +import java.util.List; + import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; @@ -42,9 +44,15 @@ public interface ConnectionFactory { /* ------------------------------------------------------------ */ /** - * @return A string representing the protocol name. + * @return A string representing the primary protocol name. */ public String getProtocol(); + + /* ------------------------------------------------------------ */ + /** + * @return A list of alternative protocol names/versions including the primary protocol. + */ + public List getProtocols(); /** *

Creates a new {@link Connection} with the given parameters

diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java index 66bff06ba74..15f0443c440 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NegotiatingServerConnectionFactory.java @@ -52,21 +52,21 @@ public abstract class NegotiatingServerConnectionFactory extends AbstractConnect } } - private final List protocols; + private final List negotiatedProtocols; private String defaultProtocol; - public NegotiatingServerConnectionFactory(String protocol, String... protocols) + public NegotiatingServerConnectionFactory(String protocol, String... negotiatedProtocols) { super(protocol); - this.protocols = new ArrayList<>(); - if (protocols != null) + this.negotiatedProtocols = new ArrayList<>(); + if (negotiatedProtocols != null) { // Trim the values, as they may come from XML configuration. - for (String p : protocols) + for (String p : negotiatedProtocols) { p = p.trim(); if (!p.isEmpty()) - this.protocols.add(p.trim()); + this.negotiatedProtocols.add(p.trim()); } } } @@ -83,34 +83,37 @@ public abstract class NegotiatingServerConnectionFactory extends AbstractConnect this.defaultProtocol = dft.isEmpty() ? null : dft; } - public List getProtocols() + public List getNegotiatedProtocols() { - return protocols; + return negotiatedProtocols; } @Override public Connection newConnection(Connector connector, EndPoint endPoint) { - List protocols = this.protocols; - if (protocols.isEmpty()) + List negotiated = this.negotiatedProtocols; + if (negotiated.isEmpty()) { - protocols = connector.getProtocols(); - Iterator i = protocols.iterator(); - while (i.hasNext()) + // Generate list of protocols that we can negotiate + negotiated = new ArrayList<>(connector.getProtocols()); + for (Iterator i = negotiated.iterator();i.hasNext();) { String protocol = i.next(); - if ("ssl".equalsIgnoreCase(protocol) || - "alpn".equalsIgnoreCase(protocol) || - "npn".equalsIgnoreCase(protocol)) + // exclude SSL and negotiating protocols + ConnectionFactory f = connector.getConnectionFactory(protocol); + + if ((f instanceof SslConnectionFactory) || + (f instanceof NegotiatingServerConnectionFactory)) { i.remove(); } } } + // if default protocol is not set, then it is the first protocol given String dft = defaultProtocol; - if (dft == null && !protocols.isEmpty()) - dft = protocols.get(0); + if (dft == null && !negotiated.isEmpty()) + dft = negotiated.get(0); SSLEngine engine = null; EndPoint ep = endPoint; @@ -123,7 +126,7 @@ public abstract class NegotiatingServerConnectionFactory extends AbstractConnect ep = null; } - return configure(newServerConnection(connector, endPoint, engine, protocols, dft), connector, endPoint); + return configure(newServerConnection(connector, endPoint, engine, negotiated, dft), connector, endPoint); } protected abstract AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol); @@ -131,6 +134,6 @@ public abstract class NegotiatingServerConnectionFactory extends AbstractConnect @Override public String toString() { - return String.format("%s@%x{%s,%s,%s}", getClass().getSimpleName(), hashCode(), getProtocol(), getDefaultProtocol(), getProtocols()); + return String.format("%s@%x{%s,%s,%s}", getClass().getSimpleName(), hashCode(), getProtocols(), getDefaultProtocol(), getNegotiatedProtocols()); } } From ce4af5a8b992863e7e372cd0b89af3baed6c17d6 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 14 Nov 2014 12:04:57 +1100 Subject: [PATCH 4/4] Implemented the PROXY protocol Added XML templates --- .../src/main/config/etc/jetty-http.xml | 4 +++ .../src/main/config/etc/jetty-ssl.xml | 4 +++ .../jetty/server/ProxyConnectionFactory.java | 31 +++++++++++++++++-- .../jetty/server/ProxyConnectionTest.java | 2 +- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/jetty-server/src/main/config/etc/jetty-http.xml b/jetty-server/src/main/config/etc/jetty-http.xml index 1459ba8645a..8456a14d4ba 100644 --- a/jetty-server/src/main/config/etc/jetty-http.xml +++ b/jetty-server/src/main/config/etc/jetty-http.xml @@ -26,6 +26,10 @@ + diff --git a/jetty-server/src/main/config/etc/jetty-ssl.xml b/jetty-server/src/main/config/etc/jetty-ssl.xml index dcb8506d2a9..1fe331f2568 100644 --- a/jetty-server/src/main/config/etc/jetty-ssl.xml +++ b/jetty-server/src/main/config/etc/jetty-ssl.xml @@ -19,6 +19,10 @@ + diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java index 78904f89de3..55baa87d1d0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ProxyConnectionFactory.java @@ -23,6 +23,7 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ReadPendingException; import java.nio.channels.WritePendingException; +import java.util.Iterator; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.Connection; @@ -44,17 +45,41 @@ public class ProxyConnectionFactory extends AbstractConnectionFactory { private static final Logger LOG = Log.getLogger(ProxyConnectionFactory.class); private final String _next; + + /* ------------------------------------------------------------ */ + /** Proxy Connection Factory that uses the next ConnectionFactory + * on the connector as the next protocol + */ + public ProxyConnectionFactory() + { + super("proxy"); + _next=null; + } public ProxyConnectionFactory(String nextProtocol) { - super("haproxy"); + super("proxy"); _next=nextProtocol; } - + @Override public Connection newConnection(Connector connector, EndPoint endp) { - return new ProxyConnection(endp,connector,_next); + String next=_next; + if (next==null) + { + for (Iterator i = connector.getProtocols().iterator();i.hasNext();) + { + String p=i.next(); + if (getProtocol().equalsIgnoreCase(p)) + { + next=i.next(); + break; + } + } + } + + return new ProxyConnection(endp,connector,next); } public static class ProxyConnection extends AbstractConnection diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java index 1593372fdbd..98c860f45ff 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ProxyConnectionTest.java @@ -43,7 +43,7 @@ public class ProxyConnectionTest http.getHttpConfiguration().setRequestHeaderSize(1024); http.getHttpConfiguration().setResponseHeaderSize(1024); - ProxyConnectionFactory proxy = new ProxyConnectionFactory(http.getProtocol()); + ProxyConnectionFactory proxy = new ProxyConnectionFactory(); _connector = new LocalConnector(_server,null,null,null,1,proxy,http); _connector.setIdleTimeout(1000);