(I know it\'s a duplicate question but the original poster asked it for the wrong reason. I\'m not implying that I\'m asking it for the right reason, but let\'s see
Based on the Answer "Yes We Can" i build the code that works with current jetty 9.3.11 and i think some would be interested.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ReadPendingException;
import java.nio.channels.WritePendingException;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
public class MyReadAheadEndpoint implements EndPoint {
/** real endpoint we are wrapping */ private final EndPoint endPoint;
/** buffer used to read start bytes */ private final ByteBuffer start ;
/** how many N start bytes to read */ private int leftToRead;
/** first N bytes */ private final byte[] bytes ;
/** buffered exception to throw next */ private IOException pendingException = null;
@Override public InetSocketAddress getLocalAddress () { return endPoint.getLocalAddress(); }
@Override public InetSocketAddress getRemoteAddress () { return endPoint.getRemoteAddress(); }
@Override public boolean isOpen () { return endPoint.isOpen(); }
@Override public long getCreatedTimeStamp () { return endPoint.getCreatedTimeStamp(); }
@Override public boolean isOutputShutdown () { return endPoint.isOutputShutdown(); }
@Override public boolean isInputShutdown () { return endPoint.isInputShutdown(); }
@Override public void shutdownOutput () { endPoint.shutdownOutput(); }
@Override public void close () { endPoint.close(); }
@Override public Object getTransport () { return endPoint.getTransport(); }
@Override public long getIdleTimeout () { return endPoint.getIdleTimeout(); }
@Override public Connection getConnection () { return endPoint.getConnection(); }
@Override public void onOpen () { endPoint.onOpen(); }
@Override public void onClose () { endPoint.onClose(); }
@Override public boolean isOptimizedForDirectBuffers() { return endPoint.isOptimizedForDirectBuffers(); }
@Override public boolean isFillInterested () { return endPoint.isFillInterested(); }
@Override public boolean flush (final ByteBuffer... v) throws IOException { return endPoint.flush(v); }
@Override public void setIdleTimeout (final long v) { endPoint.setIdleTimeout(v); }
@Override public void write (final Callback v, final ByteBuffer... b) throws WritePendingException { endPoint.write(v, b); }
@Override public void setConnection (final Connection v) { endPoint.setConnection(v); }
@Override public void upgrade (final Connection v) { endPoint.upgrade(v); }
@Override public void fillInterested (final Callback v) throws ReadPendingException { endPoint.fillInterested(v); }
@Override public int hashCode() { return endPoint.hashCode(); }
@Override public boolean equals(final Object obj) { return endPoint.equals(obj); }
@Override public String toString() { return endPoint.toString(); }
public byte[] getBytes() { if (pendingException == null) { try { readAhead(); } catch (final IOException e) { pendingException = e; } } return bytes; }
private void throwPendingException() throws IOException { if (pendingException != null) { final IOException e = pendingException; pendingException = null; throw e; } }
public MyReadAheadEndpoint(final EndPoint channel, final int readAheadLength){
this.endPoint = channel;
start = ByteBuffer.wrap(bytes = new byte[readAheadLength]);
start.flip();
leftToRead = readAheadLength;
}
private synchronized void readAhead() throws IOException {
if (leftToRead > 0) {
final int n = endPoint.fill(start);
if (n == -1) { leftToRead = -1; }
else { leftToRead -= n; }
if (leftToRead <= 0) start.rewind();
}
}
private int readFromStart(final ByteBuffer dst) throws IOException {
final int n = Math.min(dst.remaining(), start.remaining());
if (n > 0) {
dst.put(bytes, start.position(), n);
start.position(start.position() + n);
dst.flip();
}
return n;
}
@Override public synchronized int fill(final ByteBuffer dst) throws IOException {
throwPendingException();
if (leftToRead > 0) readAhead();
if (leftToRead > 0) return 0;
final int sr = start.remaining();
if (sr > 0) {
dst.compact();
final int n = readFromStart(dst);
if (n < sr) return n;
}
return sr + endPoint.fill(dst);
}
}
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.AbstractConnectionFactory;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.annotation.Name;
public class MySslConnectionFactory extends AbstractConnectionFactory {
private final SslContextFactory _sslContextFactory;
private final String _nextProtocol;
public MySslConnectionFactory() { this(HttpVersion.HTTP_1_1.asString()); }
public MySslConnectionFactory(@Name("next") final String nextProtocol) { this((SslContextFactory)null, nextProtocol); }
public MySslConnectionFactory(@Name("sslContextFactory") final SslContextFactory factory, @Name("next") final String nextProtocol) {
super("SSL");
this._sslContextFactory = factory == null?new SslContextFactory():factory;
this._nextProtocol = nextProtocol;
this.addBean(this._sslContextFactory);
}
public SslContextFactory getSslContextFactory() { return this._sslContextFactory; }
@Override protected void doStart() throws Exception {
super.doStart();
final SSLEngine engine = this._sslContextFactory.newSSLEngine();
engine.setUseClientMode(false);
final SSLSession session = engine.getSession();
if(session.getPacketBufferSize() > this.getInputBufferSize()) this.setInputBufferSize(session.getPacketBufferSize());
}
@Override public Connection newConnection(final Connector connector, final EndPoint realEndPoint) {
final MyReadAheadEndpoint aheadEndpoint = new MyReadAheadEndpoint(realEndPoint, 1);
final byte[] bytes = aheadEndpoint.getBytes();
final boolean isSSL;
if (bytes == null || bytes.length == 0) {
System.out.println("NO-Data in newConnection : "+aheadEndpoint.getRemoteAddress());
isSSL = true;
} else {
final byte b = bytes[0]; // TLS first byte is 0x16 , SSLv2 first byte is >= 0x80 , HTTP is guaranteed many bytes of ASCII
isSSL = b >= 0x7F || (b < 0x20 && b != '\n' && b != '\r' && b != '\t');
if(!isSSL) System.out.println("newConnection["+isSSL+"] : "+aheadEndpoint.getRemoteAddress());
}
final EndPoint plainEndpoint;
final SslConnection sslConnection;
if (isSSL) {
final SSLEngine engine = this._sslContextFactory.newSSLEngine(aheadEndpoint.getRemoteAddress());
engine.setUseClientMode(false);
sslConnection = this.newSslConnection(connector, aheadEndpoint, engine);
sslConnection.setRenegotiationAllowed(this._sslContextFactory.isRenegotiationAllowed());
this.configure(sslConnection, connector, aheadEndpoint);
plainEndpoint = sslConnection.getDecryptedEndPoint();
} else {
sslConnection = null;
plainEndpoint = aheadEndpoint;
}
final ConnectionFactory next = connector.getConnectionFactory(_nextProtocol);
final Connection connection = next.newConnection(connector, plainEndpoint);
plainEndpoint.setConnection(connection);
return sslConnection == null ? connection : sslConnection;
}
protected SslConnection newSslConnection(final Connector connector, final EndPoint endPoint, final SSLEngine engine) {
return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine);
}
@Override public String toString() {
return String.format("%s@%x{%s->%s}", new Object[]{this.getClass().getSimpleName(), Integer.valueOf(this.hashCode()), this.getProtocol(), this._nextProtocol});
}
}