Merge branch 'jetty-9' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project into jetty-9
This commit is contained in:
commit
0a475d034b
|
@ -38,6 +38,11 @@
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-jmx</artifactId>
|
<artifactId>jetty-jmx</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.spdy</groupId>
|
||||||
|
<artifactId>spdy-jetty-http</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
|
|
|
@ -27,8 +27,7 @@ import org.eclipse.jetty.jmx.MBeanContainer;
|
||||||
import org.eclipse.jetty.security.HashLoginService;
|
import org.eclipse.jetty.security.HashLoginService;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.HttpServerConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.NCSARequestLog;
|
import org.eclipse.jetty.server.NCSARequestLog;
|
||||||
import org.eclipse.jetty.server.SelectChannelConnector;
|
import org.eclipse.jetty.server.SelectChannelConnector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
@ -65,13 +64,11 @@ public class LikeJettyXml
|
||||||
mbContainer.addBean(new Log());
|
mbContainer.addBean(new Log());
|
||||||
|
|
||||||
// Setup Connectors
|
// Setup Connectors
|
||||||
SelectChannelConnector connector = new SelectChannelConnector(server);
|
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||||
|
http.getHttpChannelConfig().setSecurePort(8443);
|
||||||
|
SelectChannelConnector connector = new SelectChannelConnector(server,http);
|
||||||
connector.setPort(8080);
|
connector.setPort(8080);
|
||||||
connector.setIdleTimeout(30000);
|
connector.setIdleTimeout(30000);
|
||||||
HttpConfiguration httpConfiguration = new HttpConfiguration(null, false);
|
|
||||||
httpConfiguration.setConfidentialPort(8443);
|
|
||||||
connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(connector, httpConfiguration));
|
|
||||||
// TODO connector.setStatsOn(false);
|
|
||||||
|
|
||||||
server.setConnectors(new Connector[]
|
server.setConnectors(new Connector[]
|
||||||
{ connector });
|
{ connector });
|
||||||
|
@ -80,7 +77,7 @@ public class LikeJettyXml
|
||||||
sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
|
sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
|
||||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||||
sslContextFactory.setTrustStore(jetty_home + "/etc/keystore");
|
sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore");
|
||||||
sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||||
sslContextFactory.setExcludeCipherSuites(
|
sslContextFactory.setExcludeCipherSuites(
|
||||||
new String[]{
|
new String[]{
|
||||||
|
@ -94,7 +91,6 @@ public class LikeJettyXml
|
||||||
});
|
});
|
||||||
SelectChannelConnector sslConnector = new SelectChannelConnector(server,sslContextFactory);
|
SelectChannelConnector sslConnector = new SelectChannelConnector(server,sslContextFactory);
|
||||||
sslConnector.setPort(8443);
|
sslConnector.setPort(8443);
|
||||||
// TODO sslConnector.setStatsOn(false);
|
|
||||||
server.addConnector(sslConnector);
|
server.addConnector(sslConnector);
|
||||||
sslConnector.open();
|
sslConnector.open();
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,23 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.embedded;
|
package org.eclipse.jetty.embedded;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.ArrayByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
|
||||||
|
import org.eclipse.jetty.server.HttpChannelConfig;
|
||||||
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||||
import org.eclipse.jetty.server.SelectChannelConnector;
|
import org.eclipse.jetty.server.SelectChannelConnector;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||||
|
import org.eclipse.jetty.spdy.NPNServerConnectionFactory;
|
||||||
|
import org.eclipse.jetty.spdy.http.PushStrategy;
|
||||||
|
import org.eclipse.jetty.spdy.http.ReferrerPushStrategy;
|
||||||
|
import org.eclipse.jetty.spdy.http.HTTPSPDYServerConnectionFactory;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
@ -32,31 +45,73 @@ public class ManyConnectors
|
||||||
{
|
{
|
||||||
public static void main(String[] args) throws Exception
|
public static void main(String[] args) throws Exception
|
||||||
{
|
{
|
||||||
|
String jetty_home = System.getProperty("jetty.home","../jetty-server/src/main/config");
|
||||||
|
System.setProperty("jetty.home", jetty_home);
|
||||||
|
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
|
|
||||||
|
// HTTP connector
|
||||||
SelectChannelConnector connector0 = new SelectChannelConnector(server);
|
SelectChannelConnector connector0 = new SelectChannelConnector(server);
|
||||||
connector0.setPort(8080);
|
connector0.setPort(8080);
|
||||||
connector0.setIdleTimeout(30000);
|
connector0.setIdleTimeout(30000);
|
||||||
|
|
||||||
SelectChannelConnector connector1 = new SelectChannelConnector(server);
|
// HTTPS connector
|
||||||
connector1.setHost("127.0.0.1");
|
|
||||||
connector1.setPort(8888);
|
|
||||||
|
|
||||||
String jetty_home = System.getProperty("jetty.home","../jetty-distribution/target/distribution");
|
|
||||||
System.setProperty("jetty.home", jetty_home);
|
|
||||||
SslContextFactory sslContextFactory = new SslContextFactory();
|
SslContextFactory sslContextFactory = new SslContextFactory();
|
||||||
sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
|
sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore");
|
||||||
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
|
||||||
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
|
||||||
SelectChannelConnector sslConnector = new SelectChannelConnector(server,sslContextFactory);
|
|
||||||
sslConnector.setPort(8443);
|
SelectChannelConnector connector1 = new SelectChannelConnector(server,sslContextFactory);
|
||||||
|
connector1.setPort(8443);
|
||||||
server.setConnectors(new Connector[]
|
|
||||||
{ connector0, connector1, sslConnector });
|
|
||||||
|
// A verbosely fully configured connector with SSL, SPDY and HTTP
|
||||||
|
|
||||||
|
HttpChannelConfig config = new HttpChannelConfig();
|
||||||
|
config.setSecureScheme("https");
|
||||||
|
config.setSecurePort(8443);
|
||||||
|
config.setOutputBufferSize(32768);
|
||||||
|
config.setRequestHeaderSize(8192);
|
||||||
|
config.setResponseHeaderSize(8192);
|
||||||
|
config.addCustomizer(new ForwardedRequestCustomizer());
|
||||||
|
config.addCustomizer(new SecureRequestCustomizer());
|
||||||
|
|
||||||
|
HttpConnectionFactory http = new HttpConnectionFactory(config);
|
||||||
|
http.setInputBufferSize(16384);
|
||||||
|
|
||||||
|
PushStrategy push = new ReferrerPushStrategy();
|
||||||
|
HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
|
||||||
|
spdy2.setInputBufferSize(8192);
|
||||||
|
spdy2.setInitialWindowSize(32768);
|
||||||
|
|
||||||
|
HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3,config,push);
|
||||||
|
spdy2.setInputBufferSize(8192);
|
||||||
|
|
||||||
|
NPNServerConnectionFactory npn = new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getProtocol());
|
||||||
|
npn.setDefaultProtocol(http.getProtocol());
|
||||||
|
npn.setInputBufferSize(1024);
|
||||||
|
|
||||||
|
SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory,npn.getProtocol());
|
||||||
|
|
||||||
|
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||||
|
threadPool.setMaxThreads(256);
|
||||||
|
TimerScheduler scheduler = new TimerScheduler();
|
||||||
|
ByteBufferPool bufferPool= new ArrayByteBufferPool(32,4096,32768);
|
||||||
|
|
||||||
|
SelectChannelConnector connector2 = new SelectChannelConnector(server,threadPool,scheduler,bufferPool,2,2,ssl,npn,spdy3,spdy2,http);
|
||||||
|
connector2.setDefaultProtocol("ssl-npn");
|
||||||
|
connector2.setPort(8444);
|
||||||
|
connector2.setIdleTimeout(30000);
|
||||||
|
connector2.setSoLingerTime(10000);
|
||||||
|
|
||||||
|
// Set the connectors
|
||||||
|
server.setConnectors(new Connector[] { connector0, connector1, connector2 });
|
||||||
|
|
||||||
|
|
||||||
server.setHandler(new HelloHandler());
|
server.setHandler(new HelloHandler());
|
||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
|
server.dumpStdErr();
|
||||||
server.join();
|
server.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.embedded;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||||
|
|
||||||
|
public class TestXml
|
||||||
|
{
|
||||||
|
public static void main(String[] args) throws Exception
|
||||||
|
{
|
||||||
|
System.setProperty("jetty.home","../jetty-distribution/target/distribution");
|
||||||
|
XmlConfiguration.main(new String[]
|
||||||
|
{
|
||||||
|
"../jetty-jmx/src/main/config/etc/jetty-jmx.xml",
|
||||||
|
"../jetty-server/src/main/config/etc/jetty.xml",
|
||||||
|
"../jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -25,21 +26,33 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Authentication;
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
public class AuthenticationProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
|
public class AuthenticationProtocolHandler implements ProtocolHandler
|
||||||
{
|
{
|
||||||
private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(\\s*,\\s*)?(.*)", Pattern.CASE_INSENSITIVE);
|
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
|
||||||
|
private static final Pattern WWW_AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\".*", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
private final ResponseNotifier notifier = new ResponseNotifier();
|
private final ResponseNotifier notifier = new ResponseNotifier();
|
||||||
private final HttpClient client;
|
private final HttpClient client;
|
||||||
|
private final int maxContentLength;
|
||||||
|
|
||||||
public AuthenticationProtocolHandler(HttpClient client)
|
public AuthenticationProtocolHandler(HttpClient client)
|
||||||
|
{
|
||||||
|
this(client, 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationProtocolHandler(HttpClient client, int maxContentLength)
|
||||||
{
|
{
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
this.maxContentLength = maxContentLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,102 +64,133 @@ public class AuthenticationProtocolHandler extends Response.Listener.Adapter imp
|
||||||
@Override
|
@Override
|
||||||
public Response.Listener getResponseListener()
|
public Response.Listener getResponseListener()
|
||||||
{
|
{
|
||||||
return this;
|
return new AuthenticationListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AuthenticationListener extends BufferingResponseListener
|
||||||
public void onComplete(Result result)
|
|
||||||
{
|
{
|
||||||
if (!result.isFailed())
|
private AuthenticationListener()
|
||||||
{
|
{
|
||||||
List<WWWAuthenticate> wwwAuthenticates = parseWWWAuthenticate(result.getResponse());
|
super(maxContentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Request request = result.getRequest();
|
||||||
|
ContentResponse response = new HttpContentResponse(result.getResponse(), getContent());
|
||||||
|
if (result.isFailed())
|
||||||
|
{
|
||||||
|
Throwable failure = result.getFailure();
|
||||||
|
LOG.debug("Authentication challenge failed {}", failure);
|
||||||
|
forwardFailure(request, response, failure);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<WWWAuthenticate> wwwAuthenticates = parseWWWAuthenticate(response);
|
||||||
if (wwwAuthenticates.isEmpty())
|
if (wwwAuthenticates.isEmpty())
|
||||||
{
|
{
|
||||||
// TODO
|
LOG.debug("Authentication challenge without WWW-Authenticate header");
|
||||||
|
forwardFailure(request, response, new HttpResponseException("HTTP protocol violation: 401 without WWW-Authenticate header", response));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
final String uri = request.uri();
|
||||||
|
Authentication authentication = null;
|
||||||
|
WWWAuthenticate wwwAuthenticate = null;
|
||||||
|
for (WWWAuthenticate wwwAuthn : wwwAuthenticates)
|
||||||
{
|
{
|
||||||
Request request = result.getRequest();
|
authentication = client.getAuthenticationStore().findAuthentication(wwwAuthn.type, uri, wwwAuthn.realm);
|
||||||
final String uri = request.uri();
|
|
||||||
Authentication authentication = null;
|
|
||||||
for (WWWAuthenticate wwwAuthenticate : wwwAuthenticates)
|
|
||||||
{
|
|
||||||
authentication = client.getAuthenticationStore().findAuthentication(wwwAuthenticate.type, uri, wwwAuthenticate.realm);
|
|
||||||
if (authentication != null)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (authentication != null)
|
if (authentication != null)
|
||||||
{
|
{
|
||||||
final Authentication authn = authentication;
|
wwwAuthenticate = wwwAuthn;
|
||||||
authn.authenticate(request);
|
break;
|
||||||
request.send(new Adapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onComplete(Result result)
|
|
||||||
{
|
|
||||||
if (!result.isFailed())
|
|
||||||
{
|
|
||||||
Authentication.Result authnResult = new Authentication.Result(uri, authn);
|
|
||||||
client.getAuthenticationStore().addAuthenticationResult(authnResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
noAuthentication(request, result.getResponse());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (authentication == null)
|
||||||
}
|
|
||||||
|
|
||||||
private List<WWWAuthenticate> parseWWWAuthenticate(Response response)
|
|
||||||
{
|
|
||||||
List<WWWAuthenticate> result = new ArrayList<>();
|
|
||||||
List<String> values = Collections.list(response.headers().getValues(HttpHeader.WWW_AUTHENTICATE.asString()));
|
|
||||||
for (String value : values)
|
|
||||||
{
|
|
||||||
Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value);
|
|
||||||
if (matcher.matches())
|
|
||||||
{
|
{
|
||||||
String type = matcher.group(1);
|
LOG.debug("No authentication available for {}", request);
|
||||||
String realm = matcher.group(2);
|
forwardSuccess(request, response);
|
||||||
String params = matcher.group(4);
|
return;
|
||||||
WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(type, realm, params);
|
|
||||||
result.add(wwwAuthenticate);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void noAuthentication(Request request, Response response)
|
HttpConversation conversation = client.getConversation(request);
|
||||||
{
|
final Authentication.Result authnResult = authentication.authenticate(request, response, wwwAuthenticate.value, conversation);
|
||||||
HttpConversation conversation = client.getConversation(request);
|
LOG.debug("Authentication result {}", authnResult);
|
||||||
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
if (authnResult == null)
|
||||||
notifier.notifyBegin(listener, response);
|
{
|
||||||
notifier.notifyHeaders(listener, response);
|
forwardSuccess(request, response);
|
||||||
notifier.notifySuccess(listener, response);
|
return;
|
||||||
// TODO: this call here is horrid, but needed... but here it is too late for the exchange
|
}
|
||||||
// TODO: to figure out that the conversation is finished, so we need to manually do it here, no matter what.
|
|
||||||
// TODO: However, we also need to make sure that requests are not resent with the same ID
|
authnResult.apply(request);
|
||||||
// TODO: because here the connection has already been returned to the pool, so the "new" request may see
|
request.send(new Response.Listener.Empty()
|
||||||
// TODO: the same conversation but it's not really the case.
|
{
|
||||||
// TODO: perhaps the factory for requests should be the conversation ?
|
@Override
|
||||||
conversation.complete();
|
public void onSuccess(Response response)
|
||||||
notifier.notifyComplete(listener, new Result(request, response));
|
{
|
||||||
|
client.getAuthenticationStore().addAuthenticationResult(authnResult);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forwardFailure(Request request, Response response, Throwable failure)
|
||||||
|
{
|
||||||
|
HttpConversation conversation = client.getConversation(request);
|
||||||
|
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||||
|
notifier.notifyBegin(listener, response);
|
||||||
|
notifier.notifyHeaders(listener, response);
|
||||||
|
if (response instanceof ContentResponse)
|
||||||
|
notifier.notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||||
|
notifier.notifyFailure(listener, response, failure);
|
||||||
|
conversation.complete();
|
||||||
|
notifier.notifyComplete(listener, new Result(request, response, failure));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forwardSuccess(Request request, Response response)
|
||||||
|
{
|
||||||
|
HttpConversation conversation = client.getConversation(request);
|
||||||
|
Response.Listener listener = conversation.exchanges().peekFirst().listener();
|
||||||
|
notifier.notifyBegin(listener, response);
|
||||||
|
notifier.notifyHeaders(listener, response);
|
||||||
|
if (response instanceof ContentResponse)
|
||||||
|
notifier.notifyContent(listener, response, ByteBuffer.wrap(((ContentResponse)response).content()));
|
||||||
|
notifier.notifySuccess(listener, response);
|
||||||
|
conversation.complete();
|
||||||
|
notifier.notifyComplete(listener, new Result(request, response));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<WWWAuthenticate> parseWWWAuthenticate(Response response)
|
||||||
|
{
|
||||||
|
// TODO: these should be ordered by strength
|
||||||
|
List<WWWAuthenticate> result = new ArrayList<>();
|
||||||
|
List<String> values = Collections.list(response.headers().getValues(HttpHeader.WWW_AUTHENTICATE.asString()));
|
||||||
|
for (String value : values)
|
||||||
|
{
|
||||||
|
Matcher matcher = WWW_AUTHENTICATE_PATTERN.matcher(value);
|
||||||
|
if (matcher.matches())
|
||||||
|
{
|
||||||
|
String type = matcher.group(1);
|
||||||
|
String realm = matcher.group(2);
|
||||||
|
WWWAuthenticate wwwAuthenticate = new WWWAuthenticate(value, type, realm);
|
||||||
|
result.add(wwwAuthenticate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class WWWAuthenticate
|
private class WWWAuthenticate
|
||||||
{
|
{
|
||||||
|
private final String value;
|
||||||
private final String type;
|
private final String type;
|
||||||
private final String realm;
|
private final String realm;
|
||||||
private final String params;
|
|
||||||
|
|
||||||
public WWWAuthenticate(String type, String realm, String params)
|
public WWWAuthenticate(String value, String type, String realm)
|
||||||
{
|
{
|
||||||
|
this.value = value;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.params = params;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||||
public class HttpAuthenticationStore implements AuthenticationStore
|
public class HttpAuthenticationStore implements AuthenticationStore
|
||||||
{
|
{
|
||||||
private final List<Authentication> authentications = new CopyOnWriteArrayList<>();
|
private final List<Authentication> authentications = new CopyOnWriteArrayList<>();
|
||||||
private final Map<String, Authentication> results = new ConcurrentHashMap<>();
|
private final Map<String, Authentication.Result> results = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAuthentication(Authentication authentication)
|
public void addAuthentication(Authentication authentication)
|
||||||
|
@ -57,20 +57,26 @@ public class HttpAuthenticationStore implements AuthenticationStore
|
||||||
@Override
|
@Override
|
||||||
public void addAuthenticationResult(Authentication.Result result)
|
public void addAuthenticationResult(Authentication.Result result)
|
||||||
{
|
{
|
||||||
results.put(result.getURI(), result.getAuthentication());
|
results.put(result.getURI(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAuthenticationResults()
|
public void removeAuthenticationResult(Authentication.Result result)
|
||||||
|
{
|
||||||
|
results.remove(result.getURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearAuthenticationResults()
|
||||||
{
|
{
|
||||||
results.clear();
|
results.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Authentication findAuthenticationResult(String uri)
|
public Authentication.Result findAuthenticationResult(String uri)
|
||||||
{
|
{
|
||||||
// TODO: I should match the longest URI
|
// TODO: I should match the longest URI
|
||||||
for (Map.Entry<String, Authentication> entry : results.entrySet())
|
for (Map.Entry<String, Authentication.Result> entry : results.entrySet())
|
||||||
{
|
{
|
||||||
if (uri.startsWith(entry.getKey()))
|
if (uri.startsWith(entry.getKey()))
|
||||||
return entry.getValue();
|
return entry.getValue();
|
||||||
|
|
|
@ -69,6 +69,7 @@ import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
* and HTTP parameters (such as whether to follow redirects).</p>
|
* and HTTP parameters (such as whether to follow redirects).</p>
|
||||||
* <p>{@link HttpClient} transparently pools connections to servers, but allows direct control of connections
|
* <p>{@link HttpClient} transparently pools connections to servers, but allows direct control of connections
|
||||||
* for cases where this is needed.</p>
|
* for cases where this is needed.</p>
|
||||||
|
* <p>{@link HttpClient} also acts as a central configuration point for cookies, via {@link #getCookieStore()}.</p>
|
||||||
* <p>Typical usage:</p>
|
* <p>Typical usage:</p>
|
||||||
* <pre>
|
* <pre>
|
||||||
* // One liner:
|
* // One liner:
|
||||||
|
@ -264,6 +265,11 @@ public class HttpClient extends AggregateLifeCycle
|
||||||
}
|
}
|
||||||
|
|
||||||
public Destination getDestination(String scheme, String host, int port)
|
public Destination getDestination(String scheme, String host, int port)
|
||||||
|
{
|
||||||
|
return provideDestination(scheme, host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpDestination provideDestination(String scheme, String host, int port)
|
||||||
{
|
{
|
||||||
String address = address(scheme, host, port);
|
String address = address(scheme, host, port);
|
||||||
HttpDestination destination = destinations.get(address);
|
HttpDestination destination = destinations.get(address);
|
||||||
|
@ -321,7 +327,7 @@ public class HttpClient extends AggregateLifeCycle
|
||||||
if (port < 0)
|
if (port < 0)
|
||||||
port = "https".equals(scheme) ? 443 : 80;
|
port = "https".equals(scheme) ? 443 : 80;
|
||||||
|
|
||||||
getDestination(scheme, host, port).send(request, listener);
|
provideDestination(scheme, host, port).send(request, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Executor getExecutor()
|
public Executor getExecutor()
|
||||||
|
@ -438,7 +444,7 @@ public class HttpClient extends AggregateLifeCycle
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find a better method name
|
// TODO: find a better method name
|
||||||
public Response.Listener lookup(Request request, Response response)
|
protected Response.Listener lookup(Request request, Response response)
|
||||||
{
|
{
|
||||||
for (ProtocolHandler handler : handlers)
|
for (ProtocolHandler handler : handlers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -146,7 +146,7 @@ public class HttpConnection extends AbstractConnection implements Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cookies
|
// Cookies
|
||||||
List<HttpCookie> cookies = client.getCookieStore().getCookies(getDestination(), request.path());
|
List<HttpCookie> cookies = client.getCookieStore().findCookies(getDestination(), request.path());
|
||||||
StringBuilder cookieString = null;
|
StringBuilder cookieString = null;
|
||||||
for (int i = 0; i < cookies.size(); ++i)
|
for (int i = 0; i < cookies.size(); ++i)
|
||||||
{
|
{
|
||||||
|
@ -161,9 +161,9 @@ public class HttpConnection extends AbstractConnection implements Connection
|
||||||
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
|
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
Authentication authentication = client.getAuthenticationStore().findAuthenticationResult(request.uri());
|
Authentication.Result authnResult = client.getAuthenticationStore().findAuthenticationResult(request.uri());
|
||||||
if (authentication != null)
|
if (authnResult != null)
|
||||||
authentication.authenticate(request);
|
authnResult.apply(request);
|
||||||
|
|
||||||
// TODO: decoder headers
|
// TODO: decoder headers
|
||||||
|
|
||||||
|
@ -250,6 +250,11 @@ public class HttpConnection extends AbstractConnection implements Connection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void abort(HttpResponse response)
|
||||||
|
{
|
||||||
|
receiver.fail(new HttpResponseException("Response aborted", response));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class HttpCookieStore implements CookieStore
|
||||||
private final ConcurrentMap<String, Queue<HttpCookie>> allCookies = new ConcurrentHashMap<>();
|
private final ConcurrentMap<String, Queue<HttpCookie>> allCookies = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<HttpCookie> getCookies(Destination destination, String path)
|
public List<HttpCookie> findCookies(Destination destination, String path)
|
||||||
{
|
{
|
||||||
List<HttpCookie> result = new ArrayList<>();
|
List<HttpCookie> result = new ArrayList<>();
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,6 @@ public class HttpDestination implements Destination, AutoCloseable
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(Request request, Response.Listener listener)
|
public void send(Request request, Response.Listener listener)
|
||||||
{
|
{
|
||||||
if (!scheme.equals(request.scheme()))
|
if (!scheme.equals(request.scheme()))
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class HttpExchange
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.response = new HttpResponse(listener);
|
this.response = new HttpResponse(this, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpConversation conversation()
|
public HttpConversation conversation()
|
||||||
|
@ -117,6 +117,12 @@ public class HttpExchange
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void abort()
|
||||||
|
{
|
||||||
|
LOG.debug("Aborting {}", response);
|
||||||
|
connection.abort(response);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,38 +27,37 @@ import java.nio.file.Path;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.ContentDecoder;
|
|
||||||
import org.eclipse.jetty.client.api.ContentProvider;
|
import org.eclipse.jetty.client.api.ContentProvider;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
|
||||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
import org.eclipse.jetty.util.FutureCallback;
|
|
||||||
|
|
||||||
public class HttpRequest implements Request
|
public class HttpRequest implements Request
|
||||||
{
|
{
|
||||||
private static final AtomicLong ids = new AtomicLong();
|
private static final AtomicLong ids = new AtomicLong();
|
||||||
|
|
||||||
|
private final HttpFields headers = new HttpFields();
|
||||||
|
private final Fields params = new Fields();
|
||||||
private final HttpClient client;
|
private final HttpClient client;
|
||||||
private final long id;
|
private final long id;
|
||||||
private String scheme;
|
|
||||||
private final String host;
|
private final String host;
|
||||||
private final int port;
|
private final int port;
|
||||||
|
private String scheme;
|
||||||
private String path;
|
private String path;
|
||||||
private HttpMethod method;
|
private HttpMethod method;
|
||||||
private HttpVersion version;
|
private HttpVersion version;
|
||||||
private long idleTimeout;
|
private long idleTimeout;
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
private ContentProvider content;
|
private ContentProvider content;
|
||||||
private final HttpFields headers = new HttpFields();
|
private boolean followRedirects;
|
||||||
private final Fields params = new Fields();
|
private volatile boolean aborted;
|
||||||
|
|
||||||
public HttpRequest(HttpClient client, URI uri)
|
public HttpRequest(HttpClient client, URI uri)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +81,7 @@ public class HttpRequest implements Request
|
||||||
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
|
param(parts[0], parts.length < 2 ? "" : urlDecode(parts[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
followRedirects(client.isFollowRedirects());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String urlDecode(String value)
|
private String urlDecode(String value)
|
||||||
|
@ -199,9 +199,9 @@ public class HttpRequest implements Request
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Request agent(String userAgent)
|
public Request agent(String agent)
|
||||||
{
|
{
|
||||||
headers.put(HttpHeader.USER_AGENT, userAgent);
|
headers.put(HttpHeader.USER_AGENT, agent);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,15 +261,22 @@ public class HttpRequest implements Request
|
||||||
return content(new PathContentProvider(file));
|
return content(new PathContentProvider(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Override
|
||||||
|
// public Request decoder(ContentDecoder decoder)
|
||||||
|
// {
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Request decoder(ContentDecoder decoder)
|
public boolean followRedirects()
|
||||||
{
|
{
|
||||||
return this;
|
return followRedirects;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Request followRedirects(boolean follow)
|
public Request followRedirects(boolean follow)
|
||||||
{
|
{
|
||||||
|
this.followRedirects = follow;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,22 +296,9 @@ public class HttpRequest implements Request
|
||||||
@Override
|
@Override
|
||||||
public Future<ContentResponse> send()
|
public Future<ContentResponse> send()
|
||||||
{
|
{
|
||||||
final FutureCallback<ContentResponse> callback = new FutureCallback<>();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
BufferingResponseListener listener = new BufferingResponseListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onComplete(Result result)
|
|
||||||
{
|
|
||||||
super.onComplete(result);
|
|
||||||
HttpContentResponse contentResponse = new HttpContentResponse(result.getResponse(), content());
|
|
||||||
if (!result.isFailed())
|
|
||||||
callback.completed(contentResponse);
|
|
||||||
else
|
|
||||||
callback.failed(contentResponse, result.getFailure());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
send(listener);
|
send(listener);
|
||||||
return callback;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -313,6 +307,18 @@ public class HttpRequest implements Request
|
||||||
client.send(this, listener);
|
client.send(this, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void abort()
|
||||||
|
{
|
||||||
|
aborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean aborted()
|
||||||
|
{
|
||||||
|
return aborted;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,19 +16,22 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
public class ChannelHttpServer
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
|
||||||
|
public class HttpRequestException extends Throwable
|
||||||
{
|
{
|
||||||
public static void main(String[] s) throws Exception
|
private final Request request;
|
||||||
|
|
||||||
|
public HttpRequestException(String message, Request request)
|
||||||
{
|
{
|
||||||
Server server = new Server();
|
super(message);
|
||||||
SelectChannelConnector connector = new SelectChannelConnector(server);
|
this.request = request;
|
||||||
connector.setPort(8080);
|
}
|
||||||
server.addConnector(connector);
|
|
||||||
server.setHandler(new DumpHandler());
|
public Request getRequest()
|
||||||
server.start();
|
{
|
||||||
server.dumpStdErr();
|
return request;
|
||||||
server.join();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,13 +25,15 @@ import org.eclipse.jetty.http.HttpVersion;
|
||||||
public class HttpResponse implements Response
|
public class HttpResponse implements Response
|
||||||
{
|
{
|
||||||
private final HttpFields headers = new HttpFields();
|
private final HttpFields headers = new HttpFields();
|
||||||
|
private final HttpExchange exchange;
|
||||||
private final Listener listener;
|
private final Listener listener;
|
||||||
private HttpVersion version;
|
private HttpVersion version;
|
||||||
private int status;
|
private int status;
|
||||||
private String reason;
|
private String reason;
|
||||||
|
|
||||||
public HttpResponse(Response.Listener listener)
|
public HttpResponse(HttpExchange exchange, Listener listener)
|
||||||
{
|
{
|
||||||
|
this.exchange = exchange;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +86,7 @@ public class HttpResponse implements Response
|
||||||
@Override
|
@Override
|
||||||
public void abort()
|
public void abort()
|
||||||
{
|
{
|
||||||
// request.abort();
|
exchange.abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -60,12 +60,20 @@ public class HttpSender
|
||||||
|
|
||||||
public void send(HttpExchange exchange)
|
public void send(HttpExchange exchange)
|
||||||
{
|
{
|
||||||
LOG.debug("Sending {}", exchange.request());
|
Request request = exchange.request();
|
||||||
requestNotifier.notifyBegin(exchange.request());
|
if (request.aborted())
|
||||||
ContentProvider content = exchange.request().content();
|
{
|
||||||
this.contentLength = content == null ? -1 : content.length();
|
fail(new HttpRequestException("Request aborted", request));
|
||||||
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
}
|
||||||
send();
|
else
|
||||||
|
{
|
||||||
|
LOG.debug("Sending {}", request);
|
||||||
|
requestNotifier.notifyBegin(request);
|
||||||
|
ContentProvider content = request.content();
|
||||||
|
this.contentLength = content == null ? -1 : content.length();
|
||||||
|
this.contentChunks = content == null ? Collections.<ByteBuffer>emptyIterator() : content.iterator();
|
||||||
|
send();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send()
|
private void send()
|
||||||
|
@ -122,38 +130,45 @@ public class HttpSender
|
||||||
}
|
}
|
||||||
case FLUSH:
|
case FLUSH:
|
||||||
{
|
{
|
||||||
StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
|
if (request.aborted())
|
||||||
{
|
{
|
||||||
@Override
|
fail(new HttpRequestException("Request aborted", request));
|
||||||
protected void pendingCompleted()
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StatefulExecutorCallback callback = new StatefulExecutorCallback(client.getExecutor())
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected void pendingCompleted()
|
||||||
|
{
|
||||||
|
if (!committed)
|
||||||
|
committed(request);
|
||||||
|
send();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed(Throwable x)
|
||||||
|
{
|
||||||
|
fail(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (header == null)
|
||||||
|
header = BufferUtil.EMPTY_BUFFER;
|
||||||
|
if (chunk == null)
|
||||||
|
chunk = BufferUtil.EMPTY_BUFFER;
|
||||||
|
endPoint.write(null, callback, header, chunk, content);
|
||||||
|
if (callback.pending())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (callback.completed())
|
||||||
{
|
{
|
||||||
if (!committed)
|
if (!committed)
|
||||||
committed(request);
|
committed(request);
|
||||||
send();
|
|
||||||
|
releaseBuffers();
|
||||||
|
content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
||||||
|
lastContent = !contentChunks.hasNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void failed(Throwable x)
|
|
||||||
{
|
|
||||||
fail(x);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (header == null)
|
|
||||||
header = BufferUtil.EMPTY_BUFFER;
|
|
||||||
if (chunk == null)
|
|
||||||
chunk = BufferUtil.EMPTY_BUFFER;
|
|
||||||
endPoint.write(null, callback, header, chunk, content);
|
|
||||||
if (callback.pending())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (callback.completed())
|
|
||||||
{
|
|
||||||
if (!committed)
|
|
||||||
committed(request);
|
|
||||||
|
|
||||||
releaseBuffers();
|
|
||||||
content = contentChunks.hasNext() ? contentChunks.next() : BufferUtil.EMPTY_BUFFER;
|
|
||||||
lastContent = !contentChunks.hasNext();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -234,7 +249,7 @@ public class HttpSender
|
||||||
// Notify after
|
// Notify after
|
||||||
HttpExchange exchange = connection.getExchange();
|
HttpExchange exchange = connection.getExchange();
|
||||||
Request request = exchange.request();
|
Request request = exchange.request();
|
||||||
LOG.debug("Failed {}", request);
|
LOG.debug("Failed {} {}", request, failure);
|
||||||
|
|
||||||
boolean exchangeCompleted = exchange.requestComplete(false);
|
boolean exchangeCompleted = exchange.requestComplete(false);
|
||||||
if (!exchangeCompleted && !committed)
|
if (!exchangeCompleted && !committed)
|
||||||
|
|
|
@ -24,7 +24,7 @@ import org.eclipse.jetty.client.api.Result;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
|
|
||||||
public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler
|
public class RedirectProtocolHandler extends Response.Listener.Empty implements ProtocolHandler
|
||||||
{
|
{
|
||||||
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
private static final String ATTRIBUTE = RedirectProtocolHandler.class.getName() + ".redirect";
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
||||||
case 302:
|
case 302:
|
||||||
case 303:
|
case 303:
|
||||||
case 307:
|
case 307:
|
||||||
return true;
|
return request.followRedirects();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
||||||
// Copy content
|
// Copy content
|
||||||
redirect.content(request.content());
|
redirect.content(request.content());
|
||||||
|
|
||||||
redirect.send(new Adapter());
|
redirect.send(new Response.Listener.Empty());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,31 +18,67 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.Attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Authentication} represents a mechanism to authenticate requests for protected resources.
|
||||||
|
* <p />
|
||||||
|
* {@link Authentication}s are added to an {@link AuthenticationStore}, which is then
|
||||||
|
* {@link #matches(String, String, String) queried} to find the right
|
||||||
|
* {@link Authentication} mechanism to use based on its type, URI and realm, as returned by
|
||||||
|
* {@code WWW-Authenticate} response headers.
|
||||||
|
* <p />
|
||||||
|
* If an {@link Authentication} mechanism is found, it is then
|
||||||
|
* {@link #authenticate(Request, ContentResponse, String, Attributes) executed} for the given request,
|
||||||
|
* returning an {@link Authentication.Result}, which is then stored in the {@link AuthenticationStore}
|
||||||
|
* so that subsequent requests can be preemptively authenticated.
|
||||||
|
*/
|
||||||
public interface Authentication
|
public interface Authentication
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Matches {@link Authentication}s based on the given parameters
|
||||||
|
* @param type the {@link Authentication} type such as "Basic" or "Digest"
|
||||||
|
* @param uri the request URI
|
||||||
|
* @param realm the authentication realm as provided in the {@code WWW-Authenticate} response header
|
||||||
|
* @return true if this authentication matches, false otherwise
|
||||||
|
*/
|
||||||
boolean matches(String type, String uri, String realm);
|
boolean matches(String type, String uri, String realm);
|
||||||
|
|
||||||
void authenticate(Request request);
|
/**
|
||||||
|
* Executes the authentication mechanism for the given request, returning a {@link Result} that can be
|
||||||
|
* used to actually authenticate the request via {@link Result#apply(Request)}.
|
||||||
|
* <p />
|
||||||
|
* If a request for {@code "/secure"} returns a {@link Result}, then the result may be used for other
|
||||||
|
* requests such as {@code "/secure/foo"} or {@code "/secure/bar"}, unless those resources are protected
|
||||||
|
* by other realms.
|
||||||
|
*
|
||||||
|
* @param request the request to execute the authentication mechanism for
|
||||||
|
* @param response the 401 response obtained in the previous attempt to request the protected resource
|
||||||
|
* @param wwwAuthenticate the {@code WWW-Authenticate} header chosen for this authentication
|
||||||
|
* (among the many that the response may contain)
|
||||||
|
* @param context the conversation context in case the authentication needs multiple exchanges
|
||||||
|
* to be completed and information needs to be stored across exchanges
|
||||||
|
* @return the authentication result, or null if the authentication could not be performed
|
||||||
|
*/
|
||||||
|
Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context);
|
||||||
|
|
||||||
public static class Result
|
/**
|
||||||
|
* {@link Result} holds the information needed to authenticate a {@link Request} via {@link #apply(Request)}.
|
||||||
|
*/
|
||||||
|
public static interface Result
|
||||||
{
|
{
|
||||||
private final String uri;
|
/**
|
||||||
private final Authentication authentication;
|
* @return the URI of the request that has been used to generate this {@link Result}
|
||||||
|
*/
|
||||||
|
String getURI();
|
||||||
|
|
||||||
public Result(String uri, Authentication authentication)
|
/**
|
||||||
{
|
* Applies the authentication result to the given request.
|
||||||
this.uri = uri;
|
* Typically, a {@code Authorization} header is added to the request, with the right information to
|
||||||
this.authentication = authentication;
|
* successfully authenticate at the server.
|
||||||
}
|
*
|
||||||
|
* @param request the request to authenticate
|
||||||
public String getURI()
|
*/
|
||||||
{
|
void apply(Request request);
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Authentication getAuthentication()
|
|
||||||
{
|
|
||||||
return authentication;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,54 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store for {@link Authentication}s and {@link Authentication.Result}s.
|
||||||
|
*/
|
||||||
public interface AuthenticationStore
|
public interface AuthenticationStore
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @param authentication the {@link Authentication} to add
|
||||||
|
*/
|
||||||
public void addAuthentication(Authentication authentication);
|
public void addAuthentication(Authentication authentication);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param authentication the {@link Authentication} to remove
|
||||||
|
*/
|
||||||
public void removeAuthentication(Authentication authentication);
|
public void removeAuthentication(Authentication authentication);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the authentication that matches the given type (for example, "Basic" or "Digest"),
|
||||||
|
* the given request URI and the given realm.
|
||||||
|
* If no such authentication can be found, returns null.
|
||||||
|
*
|
||||||
|
* @param type the {@link Authentication} type such as "Basic" or "Digest"
|
||||||
|
* @param uri the request URI
|
||||||
|
* @param realm the authentication realm
|
||||||
|
* @return the authentication that matches the given parameters, or null
|
||||||
|
*/
|
||||||
public Authentication findAuthentication(String type, String uri, String realm);
|
public Authentication findAuthentication(String type, String uri, String realm);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param result the {@link Authentication.Result} to add
|
||||||
|
*/
|
||||||
public void addAuthenticationResult(Authentication.Result result);
|
public void addAuthenticationResult(Authentication.Result result);
|
||||||
|
|
||||||
public void removeAuthenticationResults();
|
/**
|
||||||
|
* @param result the {@link Authentication.Result} to remove
|
||||||
|
*/
|
||||||
|
public void removeAuthenticationResult(Authentication.Result result);
|
||||||
|
|
||||||
public Authentication findAuthenticationResult(String uri);
|
/**
|
||||||
|
* Removes all authentication results stored
|
||||||
|
*/
|
||||||
|
public void clearAuthenticationResults();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link Authentication.Result} that matches the given URI, or null if no
|
||||||
|
* {@link Authentication.Result}s match the given URI.
|
||||||
|
*
|
||||||
|
* @param uri the request URI
|
||||||
|
* @return the {@link Authentication.Result} that matches the given URI, or null
|
||||||
|
*/
|
||||||
|
public Authentication.Result findAuthenticationResult(String uri);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,25 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Connection} represent a connection to a {@link Destination} and allow applications to send
|
||||||
|
* requests via {@link #send(Request, Response.Listener)}.
|
||||||
|
* <p />
|
||||||
|
* {@link Connection}s are normally pooled by {@link Destination}s, but unpooled {@link Connection}s
|
||||||
|
* may be created by applications that want to do their own connection management via
|
||||||
|
* {@link Destination#newConnection()}.
|
||||||
|
*/
|
||||||
public interface Connection extends AutoCloseable
|
public interface Connection extends AutoCloseable
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Sends a request with an associated response listener.
|
||||||
|
* <p />
|
||||||
|
* {@link Request#send(Response.Listener)} will eventually call this method to send the request.
|
||||||
|
* It is exposed to allow applications to send requests via unpooled connections.
|
||||||
|
*
|
||||||
|
* @param request the request to send
|
||||||
|
* @param listener the response listener
|
||||||
|
*/
|
||||||
void send(Request request, Response.Listener listener);
|
void send(Request request, Response.Listener listener);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
public interface ContentDecoder
|
// TODO
|
||||||
|
interface ContentDecoder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,21 @@ package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
|
||||||
|
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link ContentProvider} provides a repeatable source of request content.
|
||||||
|
* <p />
|
||||||
|
* Implementations should return a new "view" over the same content every time {@link #iterator()} is invoked.
|
||||||
|
* <p />
|
||||||
|
* Applications should rely on utility classes such as {@link ByteBufferContentProvider}
|
||||||
|
* or {@link PathContentProvider}.
|
||||||
|
*/
|
||||||
public interface ContentProvider extends Iterable<ByteBuffer>
|
public interface ContentProvider extends Iterable<ByteBuffer>
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return the content length, if known, or -1 if the content length is unknown
|
||||||
|
*/
|
||||||
long length();
|
long length();
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,13 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specialized {@link Response} that can hold a limited content in memory.
|
||||||
|
*/
|
||||||
public interface ContentResponse extends Response
|
public interface ContentResponse extends Response
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return the response content
|
||||||
|
*/
|
||||||
byte[] content();
|
byte[] content();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,39 @@ package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.http.HttpCookie;
|
import org.eclipse.jetty.http.HttpCookie;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store for HTTP cookies that offers methods to match cookies for a given destination and path.
|
||||||
|
*
|
||||||
|
* @see HttpClient#getCookieStore()
|
||||||
|
*/
|
||||||
public interface CookieStore
|
public interface CookieStore
|
||||||
{
|
{
|
||||||
List<HttpCookie> getCookies(Destination destination, String path);
|
/**
|
||||||
|
* Returns the non-expired cookies that match the given destination and path,
|
||||||
|
* recursively matching parent paths (for the same domain) and parent domains
|
||||||
|
* (for the root path).
|
||||||
|
*
|
||||||
|
* @param destination the destination representing the domain
|
||||||
|
* @param path the request path
|
||||||
|
* @return the list of matching cookies
|
||||||
|
*/
|
||||||
|
List<HttpCookie> findCookies(Destination destination, String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given cookie to this store for the given destination.
|
||||||
|
* If the cookie's domain and the destination host do not match, the cookie is not added.
|
||||||
|
*
|
||||||
|
* @param destination the destination the cookie should belong to
|
||||||
|
* @param cookie the cookie to add
|
||||||
|
* @return whether the cookie has been added or not
|
||||||
|
*/
|
||||||
boolean addCookie(Destination destination, HttpCookie cookie);
|
boolean addCookie(Destination destination, HttpCookie cookie);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all the cookies from this store.
|
||||||
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,36 @@ package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Destination} represents the triple made of the {@link #scheme()}, the {@link #host()}
|
||||||
|
* and the {@link #port()}.
|
||||||
|
* <p />
|
||||||
|
* {@link Destination} holds a pool of {@link Connection}s, but allows to create unpooled
|
||||||
|
* connections if the application wants full control over connection management via {@link #newConnection()}.
|
||||||
|
* <p />
|
||||||
|
* {@link Destination}s may be obtained via {@link HttpClient#getDestination(String, String, int)}
|
||||||
|
*/
|
||||||
public interface Destination
|
public interface Destination
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return the scheme of this destination, such as "http" or "https"
|
||||||
|
*/
|
||||||
String scheme();
|
String scheme();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the host of this destination, such as "127.0.0.1" or "google.com"
|
||||||
|
*/
|
||||||
String host();
|
String host();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the port of this destination such as 80 or 443
|
||||||
|
*/
|
||||||
int port();
|
int port();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return a future to a new, unpooled, {@link Connection}
|
||||||
|
*/
|
||||||
Future<Connection> newConnection();
|
Future<Connection> newConnection();
|
||||||
|
|
||||||
void send(Request request, Response.Listener listener);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,88 +22,274 @@ import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
|
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Request} represents a HTTP request, and offers a fluent interface to customize
|
||||||
|
* various attributes such as the path, the headers, the content, etc.</p>
|
||||||
|
* <p>You can create {@link Request} objects via {@link HttpClient#newRequest(String)} and
|
||||||
|
* you can send them using either {@link #send()} for a blocking semantic, or
|
||||||
|
* {@link #send(Response.Listener)} for an asynchronous semantic.</p>
|
||||||
|
*
|
||||||
|
* @see Response
|
||||||
|
*/
|
||||||
public interface Request
|
public interface Request
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return the conversation id
|
||||||
|
*/
|
||||||
long id();
|
long id();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the scheme of this request, such as "http" or "https"
|
||||||
|
*/
|
||||||
String scheme();
|
String scheme();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param scheme the scheme of this request, such as "http" or "https"
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request scheme(String scheme);
|
Request scheme(String scheme);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the host of this request, such as "127.0.0.1" or "google.com"
|
||||||
|
*/
|
||||||
String host();
|
String host();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the port of this request such as 80 or 443
|
||||||
|
*/
|
||||||
int port();
|
int port();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the method of this request, such as GET or POST
|
||||||
|
*/
|
||||||
HttpMethod method();
|
HttpMethod method();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param method the method of this request, such as GET or POST
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request method(HttpMethod method);
|
Request method(HttpMethod method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the path of this request, such as "/"
|
||||||
|
*/
|
||||||
String path();
|
String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path the path of this request, such as "/"
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request path(String path);
|
Request path(String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the full URI of this request such as "http://host:port/path"
|
||||||
|
*/
|
||||||
String uri();
|
String uri();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the HTTP version of this request, such as "HTTP/1.1"
|
||||||
|
*/
|
||||||
HttpVersion version();
|
HttpVersion version();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param version the HTTP version of this request, such as "HTTP/1.1"
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request version(HttpVersion version);
|
Request version(HttpVersion version);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the query parameters of this request
|
||||||
|
*/
|
||||||
Fields params();
|
Fields params();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name the name of the query parameter
|
||||||
|
* @param value the value of the query parameter
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request param(String name, String value);
|
Request param(String name, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the headers of this request
|
||||||
|
*/
|
||||||
HttpFields headers();
|
HttpFields headers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name the name of the header
|
||||||
|
* @param value the value of the header
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request header(String name, String value);
|
Request header(String name, String value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the content provider of this request
|
||||||
|
*/
|
||||||
ContentProvider content();
|
ContentProvider content();
|
||||||
|
|
||||||
Request content(ContentProvider buffer);
|
/**
|
||||||
|
* @param content the content provider of this request
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
|
Request content(ContentProvider content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut method to specify a file as a content for this request, with the default content type of
|
||||||
|
* "application/octect-stream".
|
||||||
|
*
|
||||||
|
* @param file the file to upload
|
||||||
|
* @return this request object
|
||||||
|
* @throws IOException if the file does not exist or cannot be read
|
||||||
|
*/
|
||||||
Request file(Path file) throws IOException;
|
Request file(Path file) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shortcut method to specify a file as a content for this request, with the given content type.
|
||||||
|
*
|
||||||
|
* @param file the file to upload
|
||||||
|
* @param contentType the content type of the file
|
||||||
|
* @return this request object
|
||||||
|
* @throws IOException if the file does not exist or cannot be read
|
||||||
|
*/
|
||||||
Request file(Path file, String contentType) throws IOException;
|
Request file(Path file, String contentType) throws IOException;
|
||||||
|
|
||||||
Request decoder(ContentDecoder decoder);
|
// Request decoder(ContentDecoder decoder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the user agent for this request
|
||||||
|
*/
|
||||||
String agent();
|
String agent();
|
||||||
|
|
||||||
Request agent(String userAgent);
|
/**
|
||||||
|
* @param agent the user agent for this request
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
|
Request agent(String agent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the idle timeout for this request
|
||||||
|
*/
|
||||||
long idleTimeout();
|
long idleTimeout();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param timeout the idle timeout for this request
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request idleTimeout(long timeout);
|
Request idleTimeout(long timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether this request follows redirects
|
||||||
|
*/
|
||||||
|
boolean followRedirects();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param follow whether this request follows redirects
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request followRedirects(boolean follow);
|
Request followRedirects(boolean follow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the listener for request events
|
||||||
|
*/
|
||||||
Listener listener();
|
Listener listener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param listener the listener for request events
|
||||||
|
* @return this request object
|
||||||
|
*/
|
||||||
Request listener(Listener listener);
|
Request listener(Listener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends this request and returns a {@link Future} that can be used to wait for the
|
||||||
|
* request and the response to be completed (either with a success or a failure).
|
||||||
|
* <p />
|
||||||
|
* This method should be used when a simple blocking semantic is needed, and when it is known
|
||||||
|
* that the response content can be buffered without exceeding memory constraints.
|
||||||
|
* For example, this method is not appropriate to download big files from a server; consider using
|
||||||
|
* {@link #send(Response.Listener)} instead, passing your own {@link Response.Listener} or a utility
|
||||||
|
* listener such as {@link StreamingResponseListener}.
|
||||||
|
* <p />
|
||||||
|
* The future will return when {@link Response.Listener#onComplete(Result)} is invoked.
|
||||||
|
*
|
||||||
|
* @return a {@link Future} to wait on for request and response completion
|
||||||
|
* @see Response.Listener#onComplete(Result)
|
||||||
|
*/
|
||||||
Future<ContentResponse> send();
|
Future<ContentResponse> send();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends this request and asynchronously notifies the given listener for response events.
|
||||||
|
* <p />
|
||||||
|
* This method should be used when the application needs to be notified of the various response events
|
||||||
|
* as they happen, or when the application needs to efficiently manage the response content.
|
||||||
|
*
|
||||||
|
* @param listener the listener that receives response events
|
||||||
|
*/
|
||||||
void send(Response.Listener listener);
|
void send(Response.Listener listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to abort the send of this request.
|
||||||
|
*
|
||||||
|
* @see #aborted()
|
||||||
|
*/
|
||||||
|
void abort();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether {@link #abort()} was called
|
||||||
|
*/
|
||||||
|
boolean aborted();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for request events
|
||||||
|
*/
|
||||||
public interface Listener
|
public interface Listener
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the request is queued, waiting to be sent
|
||||||
|
*
|
||||||
|
* @param request the request being queued
|
||||||
|
*/
|
||||||
public void onQueued(Request request);
|
public void onQueued(Request request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the request begins being processed in order to be sent.
|
||||||
|
* This is the last opportunity to modify the request.
|
||||||
|
*
|
||||||
|
* @param request the request that begins being processed
|
||||||
|
*/
|
||||||
public void onBegin(Request request);
|
public void onBegin(Request request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the request headers (and perhaps small content) have been sent.
|
||||||
|
* The request is now committed, and in transit to the server, and further modifications to the
|
||||||
|
* request may have no effect.
|
||||||
|
* @param request the request that has been committed
|
||||||
|
*/
|
||||||
public void onHeaders(Request request);
|
public void onHeaders(Request request);
|
||||||
|
|
||||||
public void onFlush(Request request, int bytes);
|
/**
|
||||||
|
* Callback method invoked when the request has been successfully sent.
|
||||||
|
*
|
||||||
|
* @param request the request sent
|
||||||
|
*/
|
||||||
public void onSuccess(Request request);
|
public void onSuccess(Request request);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the request has failed to be sent
|
||||||
|
* @param request the request that failed
|
||||||
|
* @param failure the failure
|
||||||
|
*/
|
||||||
public void onFailure(Request request, Throwable failure);
|
public void onFailure(Request request, Throwable failure);
|
||||||
|
|
||||||
public static class Adapter implements Listener
|
/**
|
||||||
|
* An empty implementation of {@link Listener}
|
||||||
|
*/
|
||||||
|
public static class Empty implements Listener
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onQueued(Request request)
|
public void onQueued(Request request)
|
||||||
|
@ -120,11 +306,6 @@ public interface Request
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFlush(Request request, int bytes)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,38 +20,118 @@ package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>{@link Response} represents a HTTP response and offers methods to retrieve status code, HTTP version
|
||||||
|
* and headers.</p>
|
||||||
|
* <p>{@link Response} objects are passed as parameters to {@link Response.Listener} callbacks, or as
|
||||||
|
* future result of {@link Request#send()}.</p>
|
||||||
|
* <p>{@link Response} objects do not contain getters for the response content, because it may be too large
|
||||||
|
* to fit into memory.
|
||||||
|
* The response content should be retrieved via {@link Response.Listener#onContent(Response, ByteBuffer) content
|
||||||
|
* events}, or via utility classes such as {@link BufferingResponseListener}.</p>
|
||||||
|
*/
|
||||||
public interface Response
|
public interface Response
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return the response listener passed to {@link Request#send(Listener)}
|
||||||
|
*/
|
||||||
Listener listener();
|
Listener listener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the HTTP version of this response, such as "HTTP/1.1"
|
||||||
|
*/
|
||||||
HttpVersion version();
|
HttpVersion version();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the HTTP status code of this response, such as 200 or 404
|
||||||
|
*/
|
||||||
int status();
|
int status();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the HTTP reason associated to the {@link #status()}
|
||||||
|
*/
|
||||||
String reason();
|
String reason();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the headers of this response
|
||||||
|
*/
|
||||||
HttpFields headers();
|
HttpFields headers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to abort the send of this request.
|
||||||
|
*/
|
||||||
void abort();
|
void abort();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for response events
|
||||||
|
*/
|
||||||
public interface Listener
|
public interface Listener
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the response line containing HTTP version,
|
||||||
|
* HTTP status code and reason has been received and parsed.
|
||||||
|
* <p />
|
||||||
|
* This method is the best approximation to detect when the first bytes of the response arrived to the client.
|
||||||
|
*
|
||||||
|
* @param response the response containing the response line data
|
||||||
|
*/
|
||||||
public void onBegin(Response response);
|
public void onBegin(Response response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the response headers have been received and parsed.
|
||||||
|
*
|
||||||
|
* @param response the response containing the response line data and the headers
|
||||||
|
*/
|
||||||
public void onHeaders(Response response);
|
public void onHeaders(Response response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the response content has been received.
|
||||||
|
* This method may be invoked multiple times, and the {@code content} buffer must be consumed
|
||||||
|
* before returning from this method.
|
||||||
|
*
|
||||||
|
* @param response the response containing the response line data and the headers
|
||||||
|
* @param content the content bytes received
|
||||||
|
*/
|
||||||
public void onContent(Response response, ByteBuffer content);
|
public void onContent(Response response, ByteBuffer content);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the whole response has been successfully received.
|
||||||
|
*
|
||||||
|
* @param response the response containing the response line data and the headers
|
||||||
|
*/
|
||||||
public void onSuccess(Response response);
|
public void onSuccess(Response response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the response has failed in the process of being received
|
||||||
|
*
|
||||||
|
* @param response the response containing data up to the point the failure happened
|
||||||
|
* @param failure the failure happened
|
||||||
|
*/
|
||||||
public void onFailure(Response response, Throwable failure);
|
public void onFailure(Response response, Throwable failure);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method invoked when the request <em><b>and</b></em> the response have been processed,
|
||||||
|
* either successfully or not.
|
||||||
|
* <p/>
|
||||||
|
* The {@code result} parameter contains the request, the response, and eventual failures.
|
||||||
|
* <p/>
|
||||||
|
* Requests may complete <em>after</em> response, for example in case of big uploads that are
|
||||||
|
* discarded or read asynchronously by the server.
|
||||||
|
* This method is always invoked <em>after</em> {@link #onSuccess(Response)} or
|
||||||
|
* {@link #onFailure(Response, Throwable)}, and only when request indicates that it is completed.
|
||||||
|
*
|
||||||
|
* @param result the result of the request / response exchange
|
||||||
|
*/
|
||||||
public void onComplete(Result result);
|
public void onComplete(Result result);
|
||||||
|
|
||||||
public static class Adapter implements Listener
|
/**
|
||||||
|
* An empty implementation of {@link Listener}
|
||||||
|
*/
|
||||||
|
public static class Empty implements Listener
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(Response response)
|
public void onBegin(Response response)
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.api;
|
package org.eclipse.jetty.client.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a request / response exchange, containing the {@link Request}, the {@link Response}
|
||||||
|
* and eventual failures of either.
|
||||||
|
*/
|
||||||
public class Result
|
public class Result
|
||||||
{
|
{
|
||||||
private final Request request;
|
private final Request request;
|
||||||
|
@ -48,21 +52,49 @@ public class Result
|
||||||
this.responseFailure = responseFailure;
|
this.responseFailure = responseFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the request object
|
||||||
|
*/
|
||||||
public Request getRequest()
|
public Request getRequest()
|
||||||
{
|
{
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the request failure, if any
|
||||||
|
*/
|
||||||
|
public Throwable getRequestFailure()
|
||||||
|
{
|
||||||
|
return requestFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the response object
|
||||||
|
*/
|
||||||
public Response getResponse()
|
public Response getResponse()
|
||||||
{
|
{
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the response failure, if any
|
||||||
|
*/
|
||||||
|
public Throwable getResponseFailure()
|
||||||
|
{
|
||||||
|
return responseFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return whether either the response or the request failed
|
||||||
|
*/
|
||||||
public boolean isFailed()
|
public boolean isFailed()
|
||||||
{
|
{
|
||||||
return getFailure() != null;
|
return getFailure() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the response failure, if any, otherwise the request failure, if any
|
||||||
|
*/
|
||||||
public Throwable getFailure()
|
public Throwable getFailure()
|
||||||
{
|
{
|
||||||
return responseFailure != null ? responseFailure : requestFailure;
|
return responseFailure != null ? responseFailure : requestFailure;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package provides APIs, utility classes and implementation class of an asynchronous HTTP client
|
||||||
|
* <p />
|
||||||
|
* The core class is {@link HttpClient}, which acts as a central configuration object (for example
|
||||||
|
* for {@link HttpClient#setIdleTimeout(long) idle timeouts}, {@link HttpClient#setMaxConnectionsPerAddress(int)
|
||||||
|
* max connections per domain}, etc.) and as a factory for {@link Request} objects.
|
||||||
|
* <p />
|
||||||
|
* The HTTP protocol is based on the request/response paradigm, a unit that in this implementation is called
|
||||||
|
* <em>exchange</em> and is represented by {@link HttpExchange}.
|
||||||
|
* An initial request may trigger a sequence of exchanges with one or more servers, called a <em>conversation</em>
|
||||||
|
* and represented by {@link HttpConversation}. A typical example of a conversation is a redirect, where
|
||||||
|
* upon a request for a resource URI, the server replies with a redirect (for example with the 303 status code)
|
||||||
|
* to another URI. This conversation is made of a first exchange made of the original request and its 303 response,
|
||||||
|
* and of a second exchange made of the request for the new URI and its 200 response.
|
||||||
|
* <p />
|
||||||
|
* {@link HttpClient} holds a number of {@link HttpDestination destinations}, which in turn hold a number of
|
||||||
|
* pooled {@link HttpConnection connections}.
|
||||||
|
* <p />
|
||||||
|
* When a request is sent, its exchange is associated to a connection, either taken from an idle queue or created
|
||||||
|
* anew, and when both the request and response are completed, the exchange is disassociated from the connection.
|
||||||
|
* Conversation may span multiple connections on different destinations, and therefore are maintained at the
|
||||||
|
* {@link HttpClient} level.
|
||||||
|
* <p />
|
||||||
|
* Applications may decide to send the request and wait for the response in a blocking way, using
|
||||||
|
* {@link Request#send()}.
|
||||||
|
* Alternatively, application may ask to be notified of response events asynchronously, using
|
||||||
|
* {@link Request#send(Response.Listener)}.
|
||||||
|
*/
|
||||||
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
|
@ -22,8 +22,10 @@ import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.charset.UnsupportedCharsetException;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Authentication;
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.util.Attributes;
|
||||||
import org.eclipse.jetty.util.B64Code;
|
import org.eclipse.jetty.util.B64Code;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
|
||||||
|
@ -55,17 +57,48 @@ public class BasicAuthentication implements Authentication
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(Request request)
|
public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context)
|
||||||
{
|
{
|
||||||
String encoding = StringUtil.__ISO_8859_1;
|
String encoding = StringUtil.__ISO_8859_1;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
|
String value = "Basic " + B64Code.encode(user + ":" + password, encoding);
|
||||||
request.header(HttpHeader.AUTHORIZATION.asString(), value);
|
return new BasicResult(request.uri(), value);
|
||||||
}
|
}
|
||||||
catch (UnsupportedEncodingException x)
|
catch (UnsupportedEncodingException x)
|
||||||
{
|
{
|
||||||
throw new UnsupportedCharsetException(encoding);
|
throw new UnsupportedCharsetException(encoding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class BasicResult implements Result
|
||||||
|
{
|
||||||
|
private final String uri;
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public BasicResult(String uri, String value)
|
||||||
|
{
|
||||||
|
this.uri = uri;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURI()
|
||||||
|
{
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Request request)
|
||||||
|
{
|
||||||
|
if (request.uri().startsWith(uri))
|
||||||
|
request.header(HttpHeader.AUTHORIZATION.asString(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("Basic authentication result for %s", uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.client.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.HttpContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
|
||||||
|
public class BlockingResponseListener extends BufferingResponseListener implements Future<ContentResponse>
|
||||||
|
{
|
||||||
|
private final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
private ContentResponse response;
|
||||||
|
private Throwable failure;
|
||||||
|
private volatile boolean cancelled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(Response response)
|
||||||
|
{
|
||||||
|
super.onBegin(response);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Response response)
|
||||||
|
{
|
||||||
|
super.onHeaders(response);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContent(Response response, ByteBuffer content)
|
||||||
|
{
|
||||||
|
super.onContent(response, content);
|
||||||
|
if (cancelled)
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
super.onComplete(result);
|
||||||
|
response = new HttpContentResponse(result.getResponse(), getContent());
|
||||||
|
failure = result.getFailure();
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning)
|
||||||
|
{
|
||||||
|
cancelled = true;
|
||||||
|
return latch.getCount() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled()
|
||||||
|
{
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone()
|
||||||
|
{
|
||||||
|
return latch.getCount() == 0 || isCancelled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentResponse get() throws InterruptedException, ExecutionException
|
||||||
|
{
|
||||||
|
latch.await();
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ContentResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||||
|
{
|
||||||
|
boolean expired = !latch.await(timeout, unit);
|
||||||
|
if (expired)
|
||||||
|
throw new TimeoutException();
|
||||||
|
return result();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ContentResponse result() throws ExecutionException
|
||||||
|
{
|
||||||
|
if (isCancelled())
|
||||||
|
throw (CancellationException)new CancellationException().initCause(failure);
|
||||||
|
if (failure != null)
|
||||||
|
throw new ExecutionException(failure);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,38 +18,32 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client.util;
|
package org.eclipse.jetty.client.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.UnsupportedCharsetException;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
|
||||||
public class BufferingResponseListener extends Response.Listener.Adapter
|
public class BufferingResponseListener extends Response.Listener.Empty
|
||||||
{
|
{
|
||||||
private final CountDownLatch latch = new CountDownLatch(1);
|
private final int maxLength;
|
||||||
private final int maxCapacity;
|
private volatile byte[] buffer = new byte[0];
|
||||||
private Response response;
|
|
||||||
private Throwable failure;
|
|
||||||
private byte[] buffer = new byte[0];
|
|
||||||
|
|
||||||
public BufferingResponseListener()
|
public BufferingResponseListener()
|
||||||
{
|
{
|
||||||
this(16 * 1024 * 1024);
|
this(2 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferingResponseListener(int maxCapacity)
|
public BufferingResponseListener(int maxLength)
|
||||||
{
|
{
|
||||||
this.maxCapacity = maxCapacity;
|
this.maxLength = maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onContent(Response response, ByteBuffer content)
|
public void onContent(Response response, ByteBuffer content)
|
||||||
{
|
{
|
||||||
long newLength = buffer.length + content.remaining();
|
long newLength = buffer.length + content.remaining();
|
||||||
if (newLength > maxCapacity)
|
if (newLength > maxLength)
|
||||||
throw new IllegalStateException("Buffering capacity exceeded");
|
throw new IllegalStateException("Buffering capacity exceeded");
|
||||||
|
|
||||||
byte[] newBuffer = new byte[(int)newLength];
|
byte[] newBuffer = new byte[(int)newLength];
|
||||||
|
@ -58,38 +52,20 @@ public class BufferingResponseListener extends Response.Listener.Adapter
|
||||||
buffer = newBuffer;
|
buffer = newBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public byte[] getContent()
|
||||||
public void onSuccess(Response response)
|
|
||||||
{
|
|
||||||
this.response = response;
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Response response, Throwable failure)
|
|
||||||
{
|
|
||||||
this.response = response;
|
|
||||||
this.failure = failure;
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Response await(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
|
||||||
{
|
|
||||||
boolean expired = !latch.await(timeout, unit);
|
|
||||||
if (failure != null)
|
|
||||||
throw new ExecutionException(failure);
|
|
||||||
if (expired)
|
|
||||||
throw new TimeoutException();
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] content()
|
|
||||||
{
|
{
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String contentAsString(String encoding)
|
public String getContent(String encoding)
|
||||||
{
|
{
|
||||||
return new String(content(), Charset.forName(encoding));
|
try
|
||||||
|
{
|
||||||
|
return new String(getContent(), encoding);
|
||||||
|
}
|
||||||
|
catch (UnsupportedEncodingException x)
|
||||||
|
{
|
||||||
|
throw new UnsupportedCharsetException(encoding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,271 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.client.util;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.util.Attributes;
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
|
|
||||||
|
public class DigestAuthentication implements Authentication
|
||||||
|
{
|
||||||
|
private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)");
|
||||||
|
|
||||||
|
private final String uri;
|
||||||
|
private final String realm;
|
||||||
|
private final String user;
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
public DigestAuthentication(String uri, String realm, String user, String password)
|
||||||
|
{
|
||||||
|
this.uri = uri;
|
||||||
|
this.realm = realm;
|
||||||
|
this.user = user;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(String type, String uri, String realm)
|
||||||
|
{
|
||||||
|
if (!"digest".equalsIgnoreCase(type))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!uri.startsWith(this.uri))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return this.realm.equals(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result authenticate(Request request, ContentResponse response, String wwwAuthenticate, Attributes context)
|
||||||
|
{
|
||||||
|
// Avoid case sensitivity problems on the 'D' character
|
||||||
|
String type = "igest";
|
||||||
|
wwwAuthenticate = wwwAuthenticate.substring(wwwAuthenticate.indexOf(type) + type.length());
|
||||||
|
|
||||||
|
Map<String, String> params = parseParams(wwwAuthenticate);
|
||||||
|
String nonce = params.get("nonce");
|
||||||
|
if (nonce == null || nonce.length() == 0)
|
||||||
|
return null;
|
||||||
|
String opaque = params.get("opaque");
|
||||||
|
String algorithm = params.get("algorithm");
|
||||||
|
if (algorithm == null)
|
||||||
|
algorithm = "MD5";
|
||||||
|
MessageDigest digester = getMessageDigest(algorithm);
|
||||||
|
if (digester == null)
|
||||||
|
return null;
|
||||||
|
String serverQOP = params.get("qop");
|
||||||
|
String clientQOP = null;
|
||||||
|
if (serverQOP != null)
|
||||||
|
{
|
||||||
|
List<String> serverQOPValues = Arrays.asList(serverQOP.split(","));
|
||||||
|
if (serverQOPValues.contains("auth"))
|
||||||
|
clientQOP = "auth";
|
||||||
|
else if (serverQOPValues.contains("auth-int"))
|
||||||
|
clientQOP = "auth-int";
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DigestResult(request.uri(), response.content(), realm, user, password, algorithm, nonce, clientQOP, opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> parseParams(String wwwAuthenticate)
|
||||||
|
{
|
||||||
|
Map<String, String> result = new HashMap<>();
|
||||||
|
List<String> parts = splitParams(wwwAuthenticate);
|
||||||
|
for (String part : parts)
|
||||||
|
{
|
||||||
|
Matcher matcher = PARAM_PATTERN.matcher(part);
|
||||||
|
if (matcher.matches())
|
||||||
|
{
|
||||||
|
String name = matcher.group(1).trim().toLowerCase();
|
||||||
|
String value = matcher.group(2).trim();
|
||||||
|
if (value.startsWith("\"") && value.endsWith("\""))
|
||||||
|
value = value.substring(1, value.length() - 1);
|
||||||
|
result.put(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> splitParams(String paramString)
|
||||||
|
{
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
int start = 0;
|
||||||
|
for (int i = 0; i < paramString.length(); ++i)
|
||||||
|
{
|
||||||
|
int quotes = 0;
|
||||||
|
char ch = paramString.charAt(i);
|
||||||
|
switch (ch)
|
||||||
|
{
|
||||||
|
case '\\':
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
++quotes;
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
if (quotes % 2 == 0)
|
||||||
|
{
|
||||||
|
result.add(paramString.substring(start, i).trim());
|
||||||
|
start = i + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(paramString.substring(start, paramString.length()).trim());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageDigest getMessageDigest(String algorithm)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return MessageDigest.getInstance(algorithm);
|
||||||
|
}
|
||||||
|
catch (NoSuchAlgorithmException x)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DigestResult implements Result
|
||||||
|
{
|
||||||
|
private final AtomicInteger nonceCount = new AtomicInteger();
|
||||||
|
private final String uri;
|
||||||
|
private final byte[] content;
|
||||||
|
private final String realm;
|
||||||
|
private final String user;
|
||||||
|
private final String password;
|
||||||
|
private final String algorithm;
|
||||||
|
private final String nonce;
|
||||||
|
private final String qop;
|
||||||
|
private final String opaque;
|
||||||
|
|
||||||
|
public DigestResult(String uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque)
|
||||||
|
{
|
||||||
|
this.uri = uri;
|
||||||
|
this.content = content;
|
||||||
|
this.realm = realm;
|
||||||
|
this.user = user;
|
||||||
|
this.password = password;
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
this.nonce = nonce;
|
||||||
|
this.qop = qop;
|
||||||
|
this.opaque = opaque;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURI()
|
||||||
|
{
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(Request request)
|
||||||
|
{
|
||||||
|
if (!request.uri().startsWith(uri))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MessageDigest digester = getMessageDigest(algorithm);
|
||||||
|
if (digester == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Charset charset = Charset.forName("ISO-8859-1");
|
||||||
|
String A1 = user + ":" + realm + ":" + password;
|
||||||
|
String hashA1 = toHexString(digester.digest(A1.getBytes(charset)));
|
||||||
|
|
||||||
|
String A2 = request.method().asString() + ":" + request.uri();
|
||||||
|
if ("auth-int".equals(qop))
|
||||||
|
A2 += ":" + toHexString(digester.digest(content));
|
||||||
|
String hashA2 = toHexString(digester.digest(A2.getBytes(charset)));
|
||||||
|
|
||||||
|
String nonceCount;
|
||||||
|
String clientNonce;
|
||||||
|
String A3;
|
||||||
|
if (qop != null)
|
||||||
|
{
|
||||||
|
nonceCount = nextNonceCount();
|
||||||
|
clientNonce = newClientNonce();
|
||||||
|
A3 = hashA1 + ":" + nonce + ":" + nonceCount + ":" + clientNonce + ":" + qop + ":" + hashA2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nonceCount = null;
|
||||||
|
clientNonce = null;
|
||||||
|
A3 = hashA1 + ":" + nonce + ":" + hashA2;
|
||||||
|
}
|
||||||
|
String hashA3 = toHexString(digester.digest(A3.getBytes(charset)));
|
||||||
|
|
||||||
|
StringBuilder value = new StringBuilder("Digest");
|
||||||
|
value.append(" username=\"").append(user).append("\"");
|
||||||
|
value.append(", realm=\"").append(realm).append("\"");
|
||||||
|
value.append(", nonce=\"").append(nonce).append("\"");
|
||||||
|
if (opaque != null)
|
||||||
|
value.append(", opaque=\"").append(opaque).append("\"");
|
||||||
|
value.append(", algorithm=\"").append(algorithm).append("\"");
|
||||||
|
value.append(", uri=\"").append(request.uri()).append("\"");
|
||||||
|
if (qop != null)
|
||||||
|
{
|
||||||
|
value.append(", qop=\"").append(qop).append("\"");
|
||||||
|
value.append(", nc=\"").append(nonceCount).append("\"");
|
||||||
|
value.append(", cnonce=\"").append(clientNonce).append("\"");
|
||||||
|
}
|
||||||
|
value.append(", response=\"").append(hashA3).append("\"");
|
||||||
|
|
||||||
|
request.header(HttpHeader.AUTHORIZATION.asString(), value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String nextNonceCount()
|
||||||
|
{
|
||||||
|
String padding = "00000000";
|
||||||
|
String next = Integer.toHexString(nonceCount.incrementAndGet()).toLowerCase();
|
||||||
|
return padding.substring(0, padding.length() - next.length()) + next;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String newClientNonce()
|
||||||
|
{
|
||||||
|
Random random = new Random();
|
||||||
|
byte[] bytes = new byte[8];
|
||||||
|
random.nextBytes(bytes);
|
||||||
|
return toHexString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toHexString(byte[] bytes)
|
||||||
|
{
|
||||||
|
return TypeUtil.toHexString(bytes).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
|
||||||
public class StreamingResponseListener extends Response.Listener.Adapter
|
public class StreamingResponseListener extends Response.Listener.Empty
|
||||||
{
|
{
|
||||||
public Response get(long timeout, TimeUnit seconds)
|
public Response get(long timeout, TimeUnit seconds)
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,7 +49,8 @@ public class AbstractHttpClientServerTest
|
||||||
|
|
||||||
public void start(Handler handler) throws Exception
|
public void start(Handler handler) throws Exception
|
||||||
{
|
{
|
||||||
server = new Server();
|
if (server == null)
|
||||||
|
server = new Server();
|
||||||
connector = new SelectChannelConnector(server);
|
connector = new SelectChannelConnector(server);
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
server.setHandler(handler);
|
server.setHandler(handler);
|
||||||
|
@ -62,11 +63,12 @@ public class AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void destroy() throws Exception
|
public void dispose() throws Exception
|
||||||
{
|
{
|
||||||
if (client != null)
|
if (client != null)
|
||||||
client.stop();
|
client.stop();
|
||||||
if (server != null)
|
if (server != null)
|
||||||
server.stop();
|
server.stop();
|
||||||
|
server = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
|
||||||
public class EmptyHandler extends AbstractHandler
|
public class EmptyServerHandler extends AbstractHandler
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
|
@ -18,36 +18,90 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client;
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.File;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.Authentication;
|
||||||
import org.eclipse.jetty.client.api.AuthenticationStore;
|
import org.eclipse.jetty.client.api.AuthenticationStore;
|
||||||
import org.eclipse.jetty.client.api.ContentResponse;
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||||
|
import org.eclipse.jetty.client.util.DigestAuthentication;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.security.Authenticator;
|
||||||
import org.eclipse.jetty.util.B64Code;
|
import org.eclipse.jetty.security.ConstraintMapping;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.security.ConstraintSecurityHandler;
|
||||||
|
import org.eclipse.jetty.security.HashLoginService;
|
||||||
|
import org.eclipse.jetty.security.LoginService;
|
||||||
|
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
||||||
|
import org.eclipse.jetty.security.authentication.DigestAuthenticator;
|
||||||
|
import org.eclipse.jetty.server.Handler;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
|
import org.eclipse.jetty.util.security.Constraint;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
@Test
|
private String realm = "TestRealm";
|
||||||
public void test_BasicAuthentication_WithChallenge() throws Exception
|
|
||||||
{
|
|
||||||
start(new BasicAuthenticationHandler());
|
|
||||||
|
|
||||||
|
public void startBasic(Handler handler) throws Exception
|
||||||
|
{
|
||||||
|
start(new BasicAuthenticator(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startDigest(Handler handler) throws Exception
|
||||||
|
{
|
||||||
|
start(new DigestAuthenticator(), handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start(Authenticator authenticator, Handler handler) throws Exception
|
||||||
|
{
|
||||||
|
server = new Server();
|
||||||
|
File realmFile = MavenTestingUtils.getTestResourceFile("realm.properties");
|
||||||
|
LoginService loginService = new HashLoginService(realm, realmFile.getAbsolutePath());
|
||||||
|
server.addBean(loginService);
|
||||||
|
|
||||||
|
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
|
||||||
|
|
||||||
|
Constraint constraint = new Constraint();
|
||||||
|
constraint.setAuthenticate(true);
|
||||||
|
constraint.setRoles(new String[]{"*"});
|
||||||
|
ConstraintMapping mapping = new ConstraintMapping();
|
||||||
|
mapping.setPathSpec("/*");
|
||||||
|
mapping.setConstraint(constraint);
|
||||||
|
|
||||||
|
securityHandler.addConstraintMapping(mapping);
|
||||||
|
securityHandler.setAuthenticator(authenticator);
|
||||||
|
securityHandler.setLoginService(loginService);
|
||||||
|
securityHandler.setStrict(false);
|
||||||
|
|
||||||
|
securityHandler.setHandler(handler);
|
||||||
|
start(securityHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_BasicAuthentication() throws Exception
|
||||||
|
{
|
||||||
|
startBasic(new EmptyServerHandler());
|
||||||
|
test_Authentication(new BasicAuthentication("http://localhost:" + connector.getLocalPort(), realm, "basic", "basic"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_DigestAuthentication() throws Exception
|
||||||
|
{
|
||||||
|
startDigest(new EmptyServerHandler());
|
||||||
|
test_Authentication(new DigestAuthentication("http://localhost:" + connector.getLocalPort(), realm, "digest", "digest"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void test_Authentication(Authentication authentication) throws Exception
|
||||||
|
{
|
||||||
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
AuthenticationStore authenticationStore = client.getAuthenticationStore();
|
||||||
String realm = "test";
|
|
||||||
|
|
||||||
final AtomicInteger requests = new AtomicInteger();
|
final AtomicInteger requests = new AtomicInteger();
|
||||||
Request.Listener.Adapter requestListener = new Request.Listener.Adapter()
|
Request.Listener.Empty requestListener = new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
@ -58,10 +112,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
client.getRequestListeners().add(requestListener);
|
client.getRequestListeners().add(requestListener);
|
||||||
|
|
||||||
// Request without Authentication causes a 401
|
// Request without Authentication causes a 401
|
||||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
Request request = client.newRequest("localhost", connector.getLocalPort()).path("/test");
|
||||||
.path("/test")
|
|
||||||
.param("type", "Basic")
|
|
||||||
.param("realm", realm);
|
|
||||||
ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
|
ContentResponse response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(401, response.status());
|
Assert.assertEquals(401, response.status());
|
||||||
|
@ -69,11 +120,9 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
client.getRequestListeners().remove(requestListener);
|
client.getRequestListeners().remove(requestListener);
|
||||||
requests.set(0);
|
requests.set(0);
|
||||||
|
|
||||||
String user = "jetty";
|
authenticationStore.addAuthentication(authentication);
|
||||||
String password = "rocks";
|
|
||||||
authenticationStore.addAuthentication(new BasicAuthentication("http://localhost:" + connector.getLocalPort(), realm, user, password));
|
|
||||||
|
|
||||||
requestListener = new Request.Listener.Adapter()
|
requestListener = new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
@ -84,7 +133,6 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
client.getRequestListeners().add(requestListener);
|
client.getRequestListeners().add(requestListener);
|
||||||
|
|
||||||
// Request with authentication causes a 401 (no previous successful authentication) + 200
|
// Request with authentication causes a 401 (no previous successful authentication) + 200
|
||||||
request.param("user", user).param("password", password);
|
|
||||||
response = request.send().get(5, TimeUnit.SECONDS);
|
response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
|
@ -92,7 +140,7 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
client.getRequestListeners().remove(requestListener);
|
client.getRequestListeners().remove(requestListener);
|
||||||
requests.set(0);
|
requests.set(0);
|
||||||
|
|
||||||
requestListener = new Request.Listener.Adapter()
|
requestListener = new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
@ -105,65 +153,11 @@ public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest
|
||||||
// Further requests do not trigger 401 because there is a previous successful authentication
|
// Further requests do not trigger 401 because there is a previous successful authentication
|
||||||
// Remove existing header to be sure it's added by the implementation
|
// Remove existing header to be sure it's added by the implementation
|
||||||
request.header(HttpHeader.AUTHORIZATION.asString(), null);
|
request.header(HttpHeader.AUTHORIZATION.asString(), null);
|
||||||
response = request.send().get(555, TimeUnit.SECONDS);
|
response = request.send().get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
Assert.assertEquals(1, requests.get());
|
Assert.assertEquals(1, requests.get());
|
||||||
client.getRequestListeners().remove(requestListener);
|
client.getRequestListeners().remove(requestListener);
|
||||||
requests.set(0);
|
requests.set(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BasicAuthenticationHandler extends AbstractHandler
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String type = request.getParameter("type");
|
|
||||||
String authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString());
|
|
||||||
if (authorization == null)
|
|
||||||
{
|
|
||||||
String realm = request.getParameter("realm");
|
|
||||||
response.setStatus(401);
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case "Basic":
|
|
||||||
{
|
|
||||||
response.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case "Basic":
|
|
||||||
{
|
|
||||||
String user = request.getParameter("user");
|
|
||||||
String password = request.getParameter("password");
|
|
||||||
String expected = "Basic " + B64Code.encode(user + ":" + password);
|
|
||||||
if (!expected.equals(authorization))
|
|
||||||
throw new IOException(expected + " != " + authorization);
|
|
||||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,12 +49,12 @@ public class HttpClientStreamTest extends AbstractHttpClientServerTest
|
||||||
output.write(kb);
|
output.write(kb);
|
||||||
}
|
}
|
||||||
|
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
final AtomicLong requestTime = new AtomicLong();
|
final AtomicLong requestTime = new AtomicLong();
|
||||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.file(upload)
|
.file(upload)
|
||||||
.listener(new Request.Listener.Adapter()
|
.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testStoppingClosesConnections() throws Exception
|
public void testStoppingClosesConnections() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -84,14 +84,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
Assert.assertEquals(0, client.getDestinations().size());
|
Assert.assertEquals(0, client.getDestinations().size());
|
||||||
Assert.assertEquals(0, destination.getIdleConnections().size());
|
Assert.assertEquals(0, destination.getIdleConnections().size());
|
||||||
Assert.assertEquals(0, destination.getActiveConnections().size());
|
Assert.assertEquals(0, destination.getActiveConnections().size());
|
||||||
Assert.assertEquals(0, client.getCookieStore().getCookies(destination, path).size());
|
Assert.assertEquals(0, client.getCookieStore().findCookies(destination, path).size());
|
||||||
Assert.assertFalse(connection.getEndPoint().isOpen());
|
Assert.assertFalse(connection.getEndPoint().isOpen());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_DestinationCount() throws Exception
|
public void test_DestinationCount() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -111,7 +111,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_GET_ResponseWithoutContent() throws Exception
|
public void test_GET_ResponseWithoutContent() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
Response response = client.GET("http://localhost:" + connector.getLocalPort()).get(5, TimeUnit.SECONDS);
|
Response response = client.GET("http://localhost:" + connector.getLocalPort()).get(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
@ -212,14 +212,14 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_QueuedRequest_IsSent_WhenPreviousRequestSucceeded() throws Exception
|
public void test_QueuedRequest_IsSent_WhenPreviousRequestSucceeded() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
client.setMaxConnectionsPerAddress(1);
|
client.setMaxConnectionsPerAddress(1);
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(2);
|
final CountDownLatch successLatch = new CountDownLatch(2);
|
||||||
client.newRequest("http://localhost:" + connector.getLocalPort())
|
client.newRequest("http://localhost:" + connector.getLocalPort())
|
||||||
.listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
.listener(new org.eclipse.jetty.client.api.Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
||||||
|
@ -234,7 +234,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -245,7 +245,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
});
|
});
|
||||||
|
|
||||||
client.newRequest("http://localhost:" + connector.getLocalPort())
|
client.newRequest("http://localhost:" + connector.getLocalPort())
|
||||||
.listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
.listener(new org.eclipse.jetty.client.api.Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onQueued(org.eclipse.jetty.client.api.Request request)
|
public void onQueued(org.eclipse.jetty.client.api.Request request)
|
||||||
|
@ -253,7 +253,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -270,7 +270,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception
|
public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
client.setMaxConnectionsPerAddress(1);
|
client.setMaxConnectionsPerAddress(1);
|
||||||
final long idleTimeout = 1000;
|
final long idleTimeout = 1000;
|
||||||
|
@ -278,7 +278,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(3);
|
final CountDownLatch latch = new CountDownLatch(3);
|
||||||
client.newRequest("http://localhost:" + connector.getLocalPort())
|
client.newRequest("http://localhost:" + connector.getLocalPort())
|
||||||
.listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
.listener(new org.eclipse.jetty.client.api.Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
||||||
|
@ -299,7 +299,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Response response, Throwable failure)
|
public void onFailure(Response response, Throwable failure)
|
||||||
|
@ -309,7 +309,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
});
|
});
|
||||||
|
|
||||||
client.newRequest("http://localhost:" + connector.getLocalPort())
|
client.newRequest("http://localhost:" + connector.getLocalPort())
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -326,7 +326,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception
|
public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
// Prepare a big file to upload
|
// Prepare a big file to upload
|
||||||
Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();
|
Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();
|
||||||
|
@ -345,7 +345,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
final AtomicLong responseTime = new AtomicLong();
|
final AtomicLong responseTime = new AtomicLong();
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
.file(file)
|
.file(file)
|
||||||
.listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
.listener(new org.eclipse.jetty.client.api.Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(org.eclipse.jetty.client.api.Request request)
|
public void onSuccess(org.eclipse.jetty.client.api.Request request)
|
||||||
|
@ -354,7 +354,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -414,7 +414,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
return Arrays.asList(ByteBuffer.allocate(chunkSize), null).iterator();
|
return Arrays.asList(ByteBuffer.allocate(chunkSize), null).iterator();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -429,13 +429,13 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception
|
public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
final String host = "localhost";
|
final String host = "localhost";
|
||||||
final int port = connector.getLocalPort();
|
final int port = connector.getLocalPort();
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.listener(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
.listener(new org.eclipse.jetty.client.api.Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
public void onBegin(org.eclipse.jetty.client.api.Request request)
|
||||||
|
@ -444,7 +444,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
destination.getActiveConnections().peek().close();
|
destination.getActiveConnections().peek().close();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_SuccessfulRequest_ReturnsConnection() throws Exception
|
public void test_SuccessfulRequest_ReturnsConnection() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -58,7 +58,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.listener(new Request.Listener.Adapter()
|
.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
@ -66,7 +66,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
successLatch.countDown();
|
successLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Response response)
|
public void onHeaders(Response response)
|
||||||
|
@ -100,7 +100,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_FailedRequest_RemovesConnection() throws Exception
|
public void test_FailedRequest_RemovesConnection() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -115,7 +115,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||||
client.newRequest(host, port).listener(new Request.Listener.Adapter()
|
client.newRequest(host, port).listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(Request request)
|
public void onBegin(Request request)
|
||||||
|
@ -129,7 +129,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
}).send(new Response.Listener.Adapter()
|
}).send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -151,7 +151,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_BadRequest_RemovesConnection() throws Exception
|
public void test_BadRequest_RemovesConnection() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -166,7 +166,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch successLatch = new CountDownLatch(3);
|
final CountDownLatch successLatch = new CountDownLatch(3);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.listener(new Request.Listener.Adapter()
|
.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onBegin(Request request)
|
public void onBegin(Request request)
|
||||||
|
@ -181,7 +181,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
successLatch.countDown();
|
successLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -209,7 +209,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_ConnectionFailure_RemovesConnection() throws Exception
|
public void test_ConnectionFailure_RemovesConnection() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
String scheme = "http";
|
String scheme = "http";
|
||||||
String host = "localhost";
|
String host = "localhost";
|
||||||
|
@ -226,7 +226,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.listener(new Request.Listener.Adapter()
|
.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Request request, Throwable failure)
|
public void onFailure(Request request, Throwable failure)
|
||||||
|
@ -234,7 +234,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -276,7 +276,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -321,7 +321,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest(host, port)
|
client.newRequest(host, port)
|
||||||
.content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024)))
|
.content(new ByteBufferContentProvider(ByteBuffer.allocate(16 * 1024 * 1024)))
|
||||||
.send(new Response.Listener.Adapter()
|
.send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1")));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1")));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/");
|
List<HttpCookie> result = cookies.findCookies(destination, "/");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(1, result.size());
|
Assert.assertEquals(1, result.size());
|
||||||
HttpCookie cookie = result.get(0);
|
HttpCookie cookie = result.get(0);
|
||||||
|
@ -52,7 +52,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", "child.localhost", "/")));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", "child.localhost", "/")));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/");
|
List<HttpCookie> result = cookies.findCookies(destination, "/");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(1, result.size());
|
Assert.assertEquals(1, result.size());
|
||||||
HttpCookie cookie = result.get(0);
|
HttpCookie cookie = result.get(0);
|
||||||
|
@ -75,7 +75,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/path")));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/path")));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/");
|
List<HttpCookie> result = cookies.findCookies(destination, "/");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(0, result.size());
|
Assert.assertEquals(0, result.size());
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/")));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/")));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/path");
|
List<HttpCookie> result = cookies.findCookies(destination, "/path");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(1, result.size());
|
Assert.assertEquals(1, result.size());
|
||||||
HttpCookie cookie = result.get(0);
|
HttpCookie cookie = result.get(0);
|
||||||
|
@ -108,7 +108,7 @@ public class HttpCookieStoreTest
|
||||||
Destination grandChildDestination = new HttpDestination(client, "http", "grand.child.localhost.org", 80);
|
Destination grandChildDestination = new HttpDestination(client, "http", "grand.child.localhost.org", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(grandChildDestination, new HttpCookie("b", "2", null, "/")));
|
Assert.assertTrue(cookies.addCookie(grandChildDestination, new HttpCookie("b", "2", null, "/")));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(grandChildDestination, "/path");
|
List<HttpCookie> result = cookies.findCookies(grandChildDestination, "/path");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(2, result.size());
|
Assert.assertEquals(2, result.size());
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost.org", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost.org", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", 0, false, false)));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", 0, false, false)));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/");
|
List<HttpCookie> result = cookies.findCookies(destination, "/");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(0, result.size());
|
Assert.assertEquals(0, result.size());
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ public class HttpCookieStoreTest
|
||||||
Destination destination = new HttpDestination(client, "http", "localhost.org", 80);
|
Destination destination = new HttpDestination(client, "http", "localhost.org", 80);
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true)));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true)));
|
||||||
|
|
||||||
List<HttpCookie> result = cookies.getCookies(destination, "/");
|
List<HttpCookie> result = cookies.findCookies(destination, "/");
|
||||||
Assert.assertNotNull(result);
|
Assert.assertNotNull(result);
|
||||||
Assert.assertEquals(0, result.size());
|
Assert.assertEquals(0, result.size());
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,6 @@ public class HttpCookieStoreTest
|
||||||
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true)));
|
Assert.assertTrue(cookies.addCookie(destination, new HttpCookie("a", "1", null, "/", -1, false, true)));
|
||||||
|
|
||||||
cookies.clear();
|
cookies.clear();
|
||||||
Assert.assertEquals(0, cookies.getCookies(destination, "/").size());
|
Assert.assertEquals(0, cookies.findCookies(destination, "/").size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class HttpCookieTest extends AbstractHttpClientServerTest
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
|
|
||||||
Destination destination = client.getDestination(scheme, host, port);
|
Destination destination = client.getDestination(scheme, host, port);
|
||||||
List<HttpCookie> cookies = client.getCookieStore().getCookies(destination, path);
|
List<HttpCookie> cookies = client.getCookieStore().findCookies(destination, path);
|
||||||
Assert.assertNotNull(cookies);
|
Assert.assertNotNull(cookies);
|
||||||
Assert.assertEquals(1, cookies.size());
|
Assert.assertEquals(1, cookies.size());
|
||||||
HttpCookie cookie = cookies.get(0);
|
HttpCookie cookie = cookies.get(0);
|
||||||
|
|
|
@ -32,7 +32,7 @@ public class HttpDestinationTest extends AbstractHttpClientServerTest
|
||||||
@Before
|
@Before
|
||||||
public void init() throws Exception
|
public void init() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyHandler());
|
start(new EmptyServerHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -26,7 +26,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
|
@ -66,6 +66,7 @@ public class HttpReceiverTest
|
||||||
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
HttpExchange exchange = new HttpExchange(conversation, connection, null, listener);
|
||||||
conversation.exchanges().offer(exchange);
|
conversation.exchanges().offer(exchange);
|
||||||
connection.setExchange(exchange);
|
connection.setExchange(exchange);
|
||||||
|
exchange.requestComplete(true);
|
||||||
return exchange;
|
return exchange;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +79,7 @@ public class HttpReceiverTest
|
||||||
"\r\n");
|
"\r\n");
|
||||||
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
HttpExchange exchange = newExchange(new Response.Listener.Adapter()
|
HttpExchange exchange = newExchange(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -110,11 +111,11 @@ public class HttpReceiverTest
|
||||||
"Content-length: " + content.length() + "\r\n" +
|
"Content-length: " + content.length() + "\r\n" +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
content);
|
content);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
|
|
||||||
Response response = listener.await(5, TimeUnit.SECONDS);
|
Response response = listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
Assert.assertEquals("OK", response.reason());
|
Assert.assertEquals("OK", response.reason());
|
||||||
|
@ -123,7 +124,7 @@ public class HttpReceiverTest
|
||||||
Assert.assertNotNull(headers);
|
Assert.assertNotNull(headers);
|
||||||
Assert.assertEquals(1, headers.size());
|
Assert.assertEquals(1, headers.size());
|
||||||
Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
|
Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH));
|
||||||
String received = listener.contentAsString("UTF-8");
|
String received = listener.getContent("UTF-8");
|
||||||
Assert.assertEquals(content, received);
|
Assert.assertEquals(content, received);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +138,7 @@ public class HttpReceiverTest
|
||||||
"Content-length: " + (content1.length() + content2.length()) + "\r\n" +
|
"Content-length: " + (content1.length() + content2.length()) + "\r\n" +
|
||||||
"\r\n" +
|
"\r\n" +
|
||||||
content1);
|
content1);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
endPoint.setInputEOF();
|
endPoint.setInputEOF();
|
||||||
|
@ -145,7 +146,7 @@ public class HttpReceiverTest
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
@ -161,7 +162,7 @@ public class HttpReceiverTest
|
||||||
"HTTP/1.1 200 OK\r\n" +
|
"HTTP/1.1 200 OK\r\n" +
|
||||||
"Content-length: 1\r\n" +
|
"Content-length: 1\r\n" +
|
||||||
"\r\n");
|
"\r\n");
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
// Simulate an idle timeout
|
// Simulate an idle timeout
|
||||||
|
@ -169,7 +170,7 @@ public class HttpReceiverTest
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
@ -185,13 +186,13 @@ public class HttpReceiverTest
|
||||||
"HTTP/1.1 200 OK\r\n" +
|
"HTTP/1.1 200 OK\r\n" +
|
||||||
"Content-length: A\r\n" +
|
"Content-length: A\r\n" +
|
||||||
"\r\n");
|
"\r\n");
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
HttpExchange exchange = newExchange(listener);
|
HttpExchange exchange = newExchange(listener);
|
||||||
exchange.receive();
|
exchange.receive();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
listener.await(5, TimeUnit.SECONDS);
|
listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
}
|
}
|
||||||
catch (ExecutionException e)
|
catch (ExecutionException e)
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Request;
|
||||||
|
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.eclipse.jetty.util.IO;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
public class HttpRequestAbortTest extends AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testAbortOnQueued() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final AtomicBoolean begin = new AtomicBoolean();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onQueued(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBegin(Request request)
|
||||||
|
{
|
||||||
|
begin.set(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
Assert.assertFalse(begin.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnBegin() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final AtomicBoolean headers = new AtomicBoolean();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
headers.set(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
Assert.assertFalse(headers.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeaders() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
// Too late to abort
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
assertEquals(200, response.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeadersWithContent() throws Exception
|
||||||
|
{
|
||||||
|
final AtomicReference<IOException> failure = new AtomicReference<>();
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
failure.set(x);
|
||||||
|
throw x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test can behave in 2 ways:
|
||||||
|
// A) if the request is failed before the request arrived, then we get an ExecutionException
|
||||||
|
// B) if the request is failed after the request arrived, then we get a 500
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.listener(new Request.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Request request)
|
||||||
|
{
|
||||||
|
request.abort();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1}))
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public long length()
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.send().get(5, TimeUnit.SECONDS);
|
||||||
|
Assert.assertNotNull(failure.get());
|
||||||
|
assertEquals(500, response.status());
|
||||||
|
}
|
||||||
|
catch (ExecutionException x)
|
||||||
|
{
|
||||||
|
HttpRequestException xx = (HttpRequestException)x.getCause();
|
||||||
|
Request request = xx.getRequest();
|
||||||
|
Assert.assertNotNull(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,169 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.client;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.client.api.ContentResponse;
|
||||||
|
import org.eclipse.jetty.client.api.Response;
|
||||||
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class HttpResponseAbortTest extends AbstractHttpClientServerTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testAbortOnBegin() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onBegin(Response response)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnHeaders() throws Exception
|
||||||
|
{
|
||||||
|
start(new EmptyServerHandler());
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onHeaders(Response response)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbortOnContent() throws Exception
|
||||||
|
{
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
OutputStream output = response.getOutputStream();
|
||||||
|
output.write(1);
|
||||||
|
output.flush();
|
||||||
|
output.write(2);
|
||||||
|
output.flush();
|
||||||
|
}
|
||||||
|
catch (IOException ignored)
|
||||||
|
{
|
||||||
|
// The client may have already closed, and we'll get an exception here, but it's expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.send(new Response.Listener.Empty()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onContent(Response response, ByteBuffer content)
|
||||||
|
{
|
||||||
|
response.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(Result result)
|
||||||
|
{
|
||||||
|
Assert.assertTrue(result.isFailed());
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Assert.assertTrue(latch.await(555, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = CancellationException.class)
|
||||||
|
public void testCancelFuture() throws Exception
|
||||||
|
{
|
||||||
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
final AtomicReference<Future<ContentResponse>> ref = new AtomicReference<>();
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
latch.await(5, TimeUnit.SECONDS);
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
ref.get().cancel(true);
|
||||||
|
OutputStream output = response.getOutputStream();
|
||||||
|
output.write(new byte[]{0, 1, 2, 3, 4, 5, 6, 7});
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new InterruptedIOException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<ContentResponse> future = client.newRequest("localhost", connector.getLocalPort()).send();
|
||||||
|
ref.set(future);
|
||||||
|
latch.countDown();
|
||||||
|
|
||||||
|
future.get(5, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
|
@ -60,7 +60,7 @@ public class HttpSenderTest
|
||||||
Request request = client.newRequest(URI.create("http://localhost/"));
|
Request request = client.newRequest(URI.create("http://localhost/"));
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Request request)
|
public void onHeaders(Request request)
|
||||||
|
@ -121,7 +121,7 @@ public class HttpSenderTest
|
||||||
HttpConnection connection = new HttpConnection(client, endPoint, destination);
|
HttpConnection connection = new HttpConnection(client, endPoint, destination);
|
||||||
Request request = client.newRequest(URI.create("http://localhost/"));
|
Request request = client.newRequest(URI.create("http://localhost/"));
|
||||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Request request, Throwable x)
|
public void onFailure(Request request, Throwable x)
|
||||||
|
@ -129,7 +129,7 @@ public class HttpSenderTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connection.send(request, new Response.Listener.Adapter()
|
connection.send(request, new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -150,7 +150,7 @@ public class HttpSenderTest
|
||||||
HttpConnection connection = new HttpConnection(client, endPoint, destination);
|
HttpConnection connection = new HttpConnection(client, endPoint, destination);
|
||||||
Request request = client.newRequest(URI.create("http://localhost/"));
|
Request request = client.newRequest(URI.create("http://localhost/"));
|
||||||
final CountDownLatch failureLatch = new CountDownLatch(2);
|
final CountDownLatch failureLatch = new CountDownLatch(2);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(Request request, Throwable x)
|
public void onFailure(Request request, Throwable x)
|
||||||
|
@ -158,7 +158,7 @@ public class HttpSenderTest
|
||||||
failureLatch.countDown();
|
failureLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connection.send(request, new Response.Listener.Adapter()
|
connection.send(request, new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
|
@ -188,7 +188,7 @@ public class HttpSenderTest
|
||||||
request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes("UTF-8"))));
|
request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes("UTF-8"))));
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Request request)
|
public void onHeaders(Request request)
|
||||||
|
@ -223,7 +223,7 @@ public class HttpSenderTest
|
||||||
request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes("UTF-8")), ByteBuffer.wrap(content2.getBytes("UTF-8"))));
|
request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes("UTF-8")), ByteBuffer.wrap(content2.getBytes("UTF-8"))));
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Request request)
|
public void onHeaders(Request request)
|
||||||
|
@ -265,7 +265,7 @@ public class HttpSenderTest
|
||||||
});
|
});
|
||||||
final CountDownLatch headersLatch = new CountDownLatch(1);
|
final CountDownLatch headersLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch successLatch = new CountDownLatch(1);
|
final CountDownLatch successLatch = new CountDownLatch(1);
|
||||||
request.listener(new Request.Listener.Adapter()
|
request.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onHeaders(Request request)
|
public void onHeaders(Request request)
|
||||||
|
|
|
@ -27,7 +27,7 @@ import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import org.eclipse.jetty.client.HttpClient;
|
import org.eclipse.jetty.client.HttpClient;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
import org.eclipse.jetty.client.util.BlockingResponseListener;
|
||||||
import org.eclipse.jetty.client.util.PathContentProvider;
|
import org.eclipse.jetty.client.util.PathContentProvider;
|
||||||
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
import org.eclipse.jetty.client.util.StreamingResponseListener;
|
||||||
import org.eclipse.jetty.http.HttpCookie;
|
import org.eclipse.jetty.http.HttpCookie;
|
||||||
|
@ -68,7 +68,7 @@ public class Usage
|
||||||
.param("a", "b")
|
.param("a", "b")
|
||||||
.header("X-Header", "Y-value")
|
.header("X-Header", "Y-value")
|
||||||
.agent("Jetty HTTP Client")
|
.agent("Jetty HTTP Client")
|
||||||
.decoder(null)
|
// .decoder(null)
|
||||||
.content(null)
|
.content(null)
|
||||||
.idleTimeout(5000L);
|
.idleTimeout(5000L);
|
||||||
Future<ContentResponse> responseFuture = request.send();
|
Future<ContentResponse> responseFuture = request.send();
|
||||||
|
@ -82,7 +82,7 @@ public class Usage
|
||||||
HttpClient client = new HttpClient();
|
HttpClient client = new HttpClient();
|
||||||
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
final AtomicReference<Response> responseRef = new AtomicReference<>();
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
client.newRequest("localhost", 8080).send(new Response.Listener.Adapter()
|
client.newRequest("localhost", 8080).send(new Response.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Response response)
|
public void onSuccess(Response response)
|
||||||
|
@ -102,7 +102,7 @@ public class Usage
|
||||||
{
|
{
|
||||||
HttpClient client = new HttpClient();
|
HttpClient client = new HttpClient();
|
||||||
Response response = client.newRequest("localhost", 8080)
|
Response response = client.newRequest("localhost", 8080)
|
||||||
.listener(new Request.Listener.Adapter()
|
.listener(new Request.Listener.Empty()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Request request)
|
public void onSuccess(Request request)
|
||||||
|
@ -119,9 +119,9 @@ public class Usage
|
||||||
try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS))
|
try (Connection connection = client.getDestination("http", "localhost", 8080).newConnection().get(5, TimeUnit.SECONDS))
|
||||||
{
|
{
|
||||||
Request request = client.newRequest("localhost", 8080);
|
Request request = client.newRequest("localhost", 8080);
|
||||||
BufferingResponseListener listener = new BufferingResponseListener();
|
BlockingResponseListener listener = new BlockingResponseListener();
|
||||||
connection.send(request, listener);
|
connection.send(request, listener);
|
||||||
Response response = listener.await(5, TimeUnit.SECONDS);
|
Response response = listener.get(5, TimeUnit.SECONDS);
|
||||||
Assert.assertNotNull(response);
|
Assert.assertNotNull(response);
|
||||||
Assert.assertEquals(200, response.status());
|
Assert.assertEquals(200, response.status());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Format is <user>:<password>,<roles>
|
||||||
|
basic:basic
|
||||||
|
digest:digest
|
|
@ -349,6 +349,12 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-plus</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-annotations</artifactId>
|
<artifactId>jetty-annotations</artifactId>
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
# for a full listing do
|
# for a full listing do
|
||||||
# java -jar start.jar --list-options
|
# java -jar start.jar --list-options
|
||||||
#-----------------------------------------------------------
|
#-----------------------------------------------------------
|
||||||
OPTIONS=Server,jsp,resources,websocket,ext,plus
|
OPTIONS=Server,jsp,resources,websocket,ext
|
||||||
#-----------------------------------------------------------
|
#-----------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,7 +64,6 @@ etc/jetty.xml
|
||||||
# etc/jetty-ssl.xml
|
# etc/jetty-ssl.xml
|
||||||
# etc/jetty-requestlog.xml
|
# etc/jetty-requestlog.xml
|
||||||
etc/jetty-deploy.xml
|
etc/jetty-deploy.xml
|
||||||
#etc/jetty-overlay.xml
|
|
||||||
etc/jetty-webapps.xml
|
etc/jetty-webapps.xml
|
||||||
etc/jetty-contexts.xml
|
etc/jetty-contexts.xml
|
||||||
etc/jetty-testrealm.xml
|
etc/jetty-testrealm.xml
|
||||||
|
|
|
@ -47,6 +47,8 @@ import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
// TODO: Make this class inherit from oej.util.Fields
|
// TODO: Make this class inherit from oej.util.Fields
|
||||||
|
// TODO move this class to jetty-http?
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Fields. A collection of HTTP header and or Trailer fields.
|
* HTTP Fields. A collection of HTTP header and or Trailer fields.
|
||||||
|
@ -787,11 +789,11 @@ public class HttpFields implements Iterable<HttpFields.Field>
|
||||||
// Format value and params
|
// Format value and params
|
||||||
StringBuilder buf = new StringBuilder(128);
|
StringBuilder buf = new StringBuilder(128);
|
||||||
String name_value_params;
|
String name_value_params;
|
||||||
boolean quoted = QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
|
QuotedStringTokenizer.quoteIfNeeded(buf, name, delim);
|
||||||
buf.append('=');
|
buf.append('=');
|
||||||
String start=buf.toString();
|
String start=buf.toString();
|
||||||
if (value != null && value.length() > 0)
|
if (value != null && value.length() > 0)
|
||||||
quoted|=QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
|
QuotedStringTokenizer.quoteIfNeeded(buf, value, delim);
|
||||||
|
|
||||||
|
|
||||||
if (path != null && path.length() > 0)
|
if (path != null && path.length() > 0)
|
||||||
|
|
|
@ -59,9 +59,9 @@ public class HttpParser
|
||||||
CLOSED
|
CLOSED
|
||||||
};
|
};
|
||||||
|
|
||||||
private final HttpHandler _handler;
|
private final HttpHandler<ByteBuffer> _handler;
|
||||||
private final RequestHandler _requestHandler;
|
private final RequestHandler<ByteBuffer> _requestHandler;
|
||||||
private final ResponseHandler _responseHandler;
|
private final ResponseHandler<ByteBuffer> _responseHandler;
|
||||||
private final int _maxHeaderBytes;
|
private final int _maxHeaderBytes;
|
||||||
private HttpHeader _header;
|
private HttpHeader _header;
|
||||||
private String _headerString;
|
private String _headerString;
|
||||||
|
@ -91,19 +91,19 @@ public class HttpParser
|
||||||
private final Utf8StringBuilder _utf8=new Utf8StringBuilder();
|
private final Utf8StringBuilder _utf8=new Utf8StringBuilder();
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public HttpParser(RequestHandler handler)
|
public HttpParser(RequestHandler<ByteBuffer> handler)
|
||||||
{
|
{
|
||||||
this(handler,-1);
|
this(handler,-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public HttpParser(ResponseHandler handler)
|
public HttpParser(ResponseHandler<ByteBuffer> handler)
|
||||||
{
|
{
|
||||||
this(handler,-1);
|
this(handler,-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public HttpParser(RequestHandler handler,int maxHeaderBytes)
|
public HttpParser(RequestHandler<ByteBuffer> handler,int maxHeaderBytes)
|
||||||
{
|
{
|
||||||
_handler=handler;
|
_handler=handler;
|
||||||
_requestHandler=handler;
|
_requestHandler=handler;
|
||||||
|
@ -112,7 +112,7 @@ public class HttpParser
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public HttpParser(ResponseHandler handler,int maxHeaderBytes)
|
public HttpParser(ResponseHandler<ByteBuffer> handler,int maxHeaderBytes)
|
||||||
{
|
{
|
||||||
_handler=handler;
|
_handler=handler;
|
||||||
_requestHandler=null;
|
_requestHandler=null;
|
||||||
|
@ -931,12 +931,14 @@ public class HttpParser
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
if (BufferUtil.hasContent(buffer))
|
if (BufferUtil.hasContent(buffer))
|
||||||
{
|
{
|
||||||
_headerBytes+=buffer.remaining();
|
int len=buffer.remaining();
|
||||||
|
_headerBytes+=len;
|
||||||
if (_headerBytes>_maxHeaderBytes)
|
if (_headerBytes>_maxHeaderBytes)
|
||||||
{
|
{
|
||||||
|
Thread.sleep(100);
|
||||||
String chars = BufferUtil.toDetailString(buffer);
|
String chars = BufferUtil.toDetailString(buffer);
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(buffer);
|
||||||
throw new IllegalStateException(this+" data when CLOSED: "+chars);
|
throw new IllegalStateException(String.format("%s %d/%d data when CLOSED:%s",this,len,_headerBytes,chars));
|
||||||
}
|
}
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(buffer);
|
||||||
}
|
}
|
||||||
|
@ -1109,6 +1111,11 @@ public class HttpParser
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CLOSED:
|
||||||
|
{
|
||||||
|
BufferUtil.clear(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,7 +1164,7 @@ public class HttpParser
|
||||||
|
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
setState(State.END);
|
setState(State.END);
|
||||||
if (!_headResponse)
|
if (!_headResponse)
|
||||||
|
@ -1182,6 +1189,7 @@ public class HttpParser
|
||||||
}
|
}
|
||||||
setState(State.CLOSED);
|
setState(State.CLOSED);
|
||||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||||
|
_contentLength=-1;
|
||||||
_contentPosition=0;
|
_contentPosition=0;
|
||||||
_responseStatus=0;
|
_responseStatus=0;
|
||||||
_headerBytes=0;
|
_headerBytes=0;
|
||||||
|
@ -1194,6 +1202,7 @@ public class HttpParser
|
||||||
// reset state
|
// reset state
|
||||||
setState(State.START);
|
setState(State.START);
|
||||||
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
|
||||||
|
_contentLength=-1;
|
||||||
_contentPosition=0;
|
_contentPosition=0;
|
||||||
_responseStatus=0;
|
_responseStatus=0;
|
||||||
_contentChunk=null;
|
_contentChunk=null;
|
||||||
|
@ -1204,8 +1213,6 @@ public class HttpParser
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
private void setState(State state)
|
private void setState(State state)
|
||||||
{
|
{
|
||||||
if (_state==State.CLOSED && state==State.END)
|
|
||||||
new Throwable().printStackTrace();
|
|
||||||
_state=state;
|
_state=state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,9 +115,9 @@ public class HttpFieldsTest
|
||||||
header.put("name:2", "value:\r\n2");
|
header.put("name:2", "value:\r\n2");
|
||||||
|
|
||||||
ByteBuffer buffer = BufferUtil.allocate(1024);
|
ByteBuffer buffer = BufferUtil.allocate(1024);
|
||||||
buffer.clear();
|
BufferUtil.flipToFill(buffer);
|
||||||
header.putTo(buffer);
|
header.putTo(buffer);
|
||||||
buffer.flip();
|
BufferUtil.flipToFlush(buffer,0);
|
||||||
String out = BufferUtil.toString(buffer);
|
String out = BufferUtil.toString(buffer);
|
||||||
assertThat(out,containsString("name0: value??0"));
|
assertThat(out,containsString("name0: value??0"));
|
||||||
assertThat(out,containsString("name??1: value1"));
|
assertThat(out,containsString("name??1: value1"));
|
||||||
|
@ -134,9 +134,9 @@ public class HttpFieldsTest
|
||||||
header.put("CONTENT-ENCODING", "gZIP");
|
header.put("CONTENT-ENCODING", "gZIP");
|
||||||
|
|
||||||
ByteBuffer buffer = BufferUtil.allocate(1024);
|
ByteBuffer buffer = BufferUtil.allocate(1024);
|
||||||
buffer.clear();
|
BufferUtil.flipToFill(buffer);
|
||||||
header.putTo(buffer);
|
header.putTo(buffer);
|
||||||
buffer.flip();
|
BufferUtil.flipToFlush(buffer,0);
|
||||||
String out = BufferUtil.toString(buffer);
|
String out = BufferUtil.toString(buffer);
|
||||||
|
|
||||||
Assert.assertThat(out,Matchers.containsString(HttpHeader.CONNECTION+": "+HttpHeaderValue.KEEP_ALIVE));
|
Assert.assertThat(out,Matchers.containsString(HttpHeader.CONNECTION+": "+HttpHeaderValue.KEEP_ALIVE));
|
||||||
|
|
|
@ -45,6 +45,8 @@ public abstract class AbstractConnection implements Connection
|
||||||
private enum State {IDLE,INTERESTED,FILLING,FILLING_INTERESTED};
|
private enum State {IDLE,INTERESTED,FILLING,FILLING_INTERESTED};
|
||||||
private final AtomicReference<State> _state = new AtomicReference<>(State.IDLE);
|
private final AtomicReference<State> _state = new AtomicReference<>(State.IDLE);
|
||||||
|
|
||||||
|
private int _inputBufferSize=8192;
|
||||||
|
|
||||||
public AbstractConnection(EndPoint endp, Executor executor)
|
public AbstractConnection(EndPoint endp, Executor executor)
|
||||||
{
|
{
|
||||||
this(endp, executor, true);
|
this(endp, executor, true);
|
||||||
|
@ -117,6 +119,16 @@ public abstract class AbstractConnection implements Connection
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getInputBufferSize()
|
||||||
|
{
|
||||||
|
return _inputBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputBufferSize(int inputBufferSize)
|
||||||
|
{
|
||||||
|
_inputBufferSize = inputBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
public Executor getExecutor()
|
public Executor getExecutor()
|
||||||
{
|
{
|
||||||
|
@ -146,7 +158,6 @@ public abstract class AbstractConnection implements Connection
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILLING:
|
case FILLING:
|
||||||
|
|
||||||
if (_state.compareAndSet(State.FILLING,State.FILLING_INTERESTED))
|
if (_state.compareAndSet(State.FILLING,State.FILLING_INTERESTED))
|
||||||
break loop;
|
break loop;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -159,12 +159,6 @@ public abstract class AbstractEndPoint implements EndPoint
|
||||||
_writeFlusher.write(context, callback, buffers);
|
_writeFlusher.write(context, callback, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBufferingOutput()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void onIncompleteFlush();
|
protected abstract void onIncompleteFlush();
|
||||||
|
|
||||||
protected abstract boolean needsFill() throws IOException;
|
protected abstract boolean needsFill() throws IOException;
|
||||||
|
|
|
@ -321,14 +321,15 @@ public class ByteArrayEndPoint extends AbstractEndPoint
|
||||||
* @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
|
* @see org.eclipse.io.EndPoint#flush(org.eclipse.io.Buffer, org.eclipse.io.Buffer, org.eclipse.io.Buffer)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int flush(ByteBuffer... buffers) throws IOException
|
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||||
{
|
{
|
||||||
if (_closed)
|
if (_closed)
|
||||||
throw new IOException("CLOSED");
|
throw new IOException("CLOSED");
|
||||||
if (_oshut)
|
if (_oshut)
|
||||||
throw new IOException("OSHUT");
|
throw new IOException("OSHUT");
|
||||||
|
|
||||||
int flushed=0;
|
boolean flushed=true;
|
||||||
|
boolean idle=true;
|
||||||
|
|
||||||
for (ByteBuffer b : buffers)
|
for (ByteBuffer b : buffers)
|
||||||
{
|
{
|
||||||
|
@ -345,13 +346,17 @@ public class ByteArrayEndPoint extends AbstractEndPoint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flushed+=BufferUtil.flipPutFlip(b,_out);
|
if (BufferUtil.flipPutFlip(b,_out)>0)
|
||||||
|
idle=false;
|
||||||
|
|
||||||
if (BufferUtil.hasContent(b))
|
if (BufferUtil.hasContent(b))
|
||||||
|
{
|
||||||
|
flushed=false;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flushed>0)
|
if (!idle)
|
||||||
notIdle();
|
notIdle();
|
||||||
return flushed;
|
return flushed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
* Channel End Point.
|
* Channel End Point.
|
||||||
* <p>Holds the channel and socket for an NIO endpoint.
|
* <p>Holds the channel and socket for an NIO endpoint.
|
||||||
*/
|
*/
|
||||||
public class ChannelEndPoint extends AbstractEndPoint
|
public class ChannelEndPoint extends AbstractEndPoint implements SocketBased
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
|
private static final Logger LOG = Log.getLogger(ChannelEndPoint.class);
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ public class ChannelEndPoint extends AbstractEndPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int flush(ByteBuffer... buffers) throws IOException
|
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||||
{
|
{
|
||||||
int flushed=0;
|
int flushed=0;
|
||||||
try
|
try
|
||||||
|
@ -172,7 +172,7 @@ public class ChannelEndPoint extends AbstractEndPoint
|
||||||
int l=_channel.write(b);
|
int l=_channel.write(b);
|
||||||
if (l>0)
|
if (l>0)
|
||||||
flushed+=l;
|
flushed+=l;
|
||||||
else
|
if (b.hasRemaining())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,16 +183,23 @@ public class ChannelEndPoint extends AbstractEndPoint
|
||||||
{
|
{
|
||||||
throw new EofException(e);
|
throw new EofException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean all_flushed=true;
|
||||||
if (flushed>0)
|
if (flushed>0)
|
||||||
{
|
{
|
||||||
notIdle();
|
notIdle();
|
||||||
|
|
||||||
// clear empty buffers to prevent position creeping up the buffer
|
// clear empty buffers to prevent position creeping up the buffer
|
||||||
for (ByteBuffer b : buffers)
|
for (ByteBuffer b : buffers)
|
||||||
|
{
|
||||||
if (BufferUtil.isEmpty(b))
|
if (BufferUtil.isEmpty(b))
|
||||||
BufferUtil.clear(b);
|
BufferUtil.clear(b);
|
||||||
|
else
|
||||||
|
all_flushed=false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return flushed;
|
|
||||||
|
return all_flushed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteChannel getChannel()
|
public ByteChannel getChannel()
|
||||||
|
@ -206,6 +213,7 @@ public class ChannelEndPoint extends AbstractEndPoint
|
||||||
return _channel;
|
return _channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Socket getSocket()
|
public Socket getSocket()
|
||||||
{
|
{
|
||||||
return _socket;
|
return _socket;
|
||||||
|
|
|
@ -170,11 +170,12 @@ public interface EndPoint extends Closeable
|
||||||
* Flush data from the passed header/buffer to this endpoint. As many bytes as can be consumed
|
* Flush data from the passed header/buffer to this endpoint. As many bytes as can be consumed
|
||||||
* are taken from the header/buffer position up until the buffer limit. The header/buffers position
|
* are taken from the header/buffer position up until the buffer limit. The header/buffers position
|
||||||
* is updated to indicate how many bytes have been consumed.
|
* is updated to indicate how many bytes have been consumed.
|
||||||
|
* @return True IFF all the buffers have been consumed and the endpoint has flushed the data to its
|
||||||
|
* destination (ie is not buffering any data).
|
||||||
*
|
*
|
||||||
* @return the number of bytes written
|
|
||||||
* @throws EofException If the endpoint is closed or output is shutdown.
|
* @throws EofException If the endpoint is closed or output is shutdown.
|
||||||
*/
|
*/
|
||||||
int flush(ByteBuffer... buffer) throws IOException;
|
boolean flush(ByteBuffer... buffer) throws IOException;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
@ -242,9 +243,4 @@ public interface EndPoint extends Closeable
|
||||||
void onClose();
|
void onClose();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return True if the endpoint is buffering output.
|
|
||||||
*/
|
|
||||||
boolean isBufferingOutput();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,23 +49,22 @@ public class NetworkTrafficSelectChannelEndPoint extends SelectChannelEndPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int flush(ByteBuffer... buffers) throws IOException
|
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||||
{
|
{
|
||||||
int written=0;
|
boolean flushed=true;
|
||||||
for (ByteBuffer b : buffers)
|
for (ByteBuffer b : buffers)
|
||||||
{
|
{
|
||||||
if (b.hasRemaining())
|
if (b.hasRemaining())
|
||||||
{
|
{
|
||||||
int position = b.position();
|
int position = b.position();
|
||||||
int l = super.flush(b);
|
flushed|=super.flush(b);
|
||||||
|
int l=b.position()-position;
|
||||||
notifyOutgoing(b, position, l);
|
notifyOutgoing(b, position, l);
|
||||||
if (l==0)
|
if (!flushed)
|
||||||
break;
|
break;
|
||||||
else
|
|
||||||
written+=l;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return written;
|
return flushed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
|
||||||
Connection old = getConnection();
|
Connection old = getConnection();
|
||||||
super.setConnection(connection);
|
super.setConnection(connection);
|
||||||
if (old != null && old != connection)
|
if (old != null && old != connection)
|
||||||
|
{
|
||||||
|
LOG.debug("Upgrading connection {} -> {} on endPoint {}", old, connection, this);
|
||||||
_selector.getSelectorManager().connectionUpgraded(this, old);
|
_selector.getSelectorManager().connectionUpgraded(this, old);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.io;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for Socket based I/O
|
||||||
|
*/
|
||||||
|
public interface SocketBased
|
||||||
|
{
|
||||||
|
public Socket getSocket();
|
||||||
|
}
|
|
@ -297,12 +297,12 @@ abstract public class WriteFlusher
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_endPoint.flush(buffers);
|
boolean flushed=_endPoint.flush(buffers);
|
||||||
|
|
||||||
// Are we complete?
|
// Are we complete?
|
||||||
for (ByteBuffer b : buffers)
|
for (ByteBuffer b : buffers)
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b))
|
if (!flushed||BufferUtil.hasContent(b))
|
||||||
{
|
{
|
||||||
PendingState<?> pending=new PendingState<>(buffers, context, callback);
|
PendingState<?> pending=new PendingState<>(buffers, context, callback);
|
||||||
if (updateState(__WRITING,pending))
|
if (updateState(__WRITING,pending))
|
||||||
|
@ -312,17 +312,6 @@ abstract public class WriteFlusher
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle buffering endpoint
|
|
||||||
if (_endPoint.isBufferingOutput())
|
|
||||||
{
|
|
||||||
PendingState<?> pending=new PendingState<>(buffers, context, callback);
|
|
||||||
if (updateState(__WRITING,pending))
|
|
||||||
onIncompleteFlushed();
|
|
||||||
else
|
|
||||||
fail(new PendingState<>(buffers, context, callback));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If updateState didn't succeed, we don't care as our buffers have been written
|
// If updateState didn't succeed, we don't care as our buffers have been written
|
||||||
if (!updateState(__WRITING,__IDLE))
|
if (!updateState(__WRITING,__IDLE))
|
||||||
|
@ -368,12 +357,12 @@ abstract public class WriteFlusher
|
||||||
{
|
{
|
||||||
ByteBuffer[] buffers = pending.getBuffers();
|
ByteBuffer[] buffers = pending.getBuffers();
|
||||||
|
|
||||||
_endPoint.flush(buffers);
|
boolean flushed=_endPoint.flush(buffers);
|
||||||
|
|
||||||
// Are we complete?
|
// Are we complete?
|
||||||
for (ByteBuffer b : buffers)
|
for (ByteBuffer b : buffers)
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b))
|
if (!flushed || BufferUtil.hasContent(b))
|
||||||
{
|
{
|
||||||
if (updateState(__COMPLETING,pending))
|
if (updateState(__COMPLETING,pending))
|
||||||
onIncompleteFlushed();
|
onIncompleteFlushed();
|
||||||
|
@ -383,16 +372,6 @@ abstract public class WriteFlusher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle buffering endpoint
|
|
||||||
if (_endPoint.isBufferingOutput())
|
|
||||||
{
|
|
||||||
if (updateState(__COMPLETING,pending))
|
|
||||||
onIncompleteFlushed();
|
|
||||||
else
|
|
||||||
fail(pending);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If updateState didn't succeed, we don't care as our buffers have been written
|
// If updateState didn't succeed, we don't care as our buffers have been written
|
||||||
if (!updateState(__COMPLETING,__IDLE))
|
if (!updateState(__COMPLETING,__IDLE))
|
||||||
ignoreFail();
|
ignoreFail();
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
package org.eclipse.jetty.io.ssl;
|
package org.eclipse.jetty.io.ssl;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.SocketException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
|
@ -31,12 +33,14 @@ import javax.net.ssl.SSLException;
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.AbstractEndPoint;
|
import org.eclipse.jetty.io.AbstractEndPoint;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
|
import org.eclipse.jetty.io.ChannelEndPoint;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.EofException;
|
import org.eclipse.jetty.io.EofException;
|
||||||
import org.eclipse.jetty.io.FillInterest;
|
import org.eclipse.jetty.io.FillInterest;
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||||
|
import org.eclipse.jetty.io.SocketBased;
|
||||||
import org.eclipse.jetty.io.WriteFlusher;
|
import org.eclipse.jetty.io.WriteFlusher;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
@ -111,6 +115,18 @@ public class SslConnection extends AbstractConnection
|
||||||
this._bufferPool = byteBufferPool;
|
this._bufferPool = byteBufferPool;
|
||||||
this._sslEngine = sslEngine;
|
this._sslEngine = sslEngine;
|
||||||
this._decryptedEndPoint = new DecryptedEndPoint();
|
this._decryptedEndPoint = new DecryptedEndPoint();
|
||||||
|
|
||||||
|
if (endPoint instanceof SocketBased)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
((SocketBased) endPoint).getSocket().setSoLinger(true,30000);
|
||||||
|
}
|
||||||
|
catch (SocketException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeIOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SSLEngine getSSLEngine()
|
public SSLEngine getSSLEngine()
|
||||||
|
@ -397,9 +413,18 @@ public class SslConnection extends AbstractConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isBufferingOutput()
|
public void setConnection(Connection connection)
|
||||||
{
|
{
|
||||||
return BufferUtil.hasContent(_encryptedOutput);
|
if (connection instanceof AbstractConnection)
|
||||||
|
{
|
||||||
|
AbstractConnection a = (AbstractConnection)connection;
|
||||||
|
if (a.getInputBufferSize()<_sslEngine.getSession().getApplicationBufferSize());
|
||||||
|
a.setInputBufferSize(_sslEngine.getSession().getApplicationBufferSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.onOpen();
|
||||||
|
|
||||||
|
super.setConnection(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SslConnection getSslConnection()
|
public SslConnection getSslConnection()
|
||||||
|
@ -613,7 +638,7 @@ public class SslConnection extends AbstractConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized int flush(ByteBuffer... appOuts) throws IOException
|
public synchronized boolean flush(ByteBuffer... appOuts) throws IOException
|
||||||
{
|
{
|
||||||
// TODO remove this when we are certain it is OK
|
// TODO remove this when we are certain it is OK
|
||||||
if (Thread.currentThread().getName().contains("selector"))
|
if (Thread.currentThread().getName().contains("selector"))
|
||||||
|
@ -631,7 +656,7 @@ public class SslConnection extends AbstractConnection
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_cannotAcceptMoreAppDataToFlush)
|
if (_cannotAcceptMoreAppDataToFlush)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
// We will need a network buffer
|
// We will need a network buffer
|
||||||
if (_encryptedOutput == null)
|
if (_encryptedOutput == null)
|
||||||
|
@ -647,13 +672,16 @@ public class SslConnection extends AbstractConnection
|
||||||
LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
|
LOG.debug("{} wrap {}", SslConnection.this, wrapResult);
|
||||||
BufferUtil.flipToFlush(_encryptedOutput, pos);
|
BufferUtil.flipToFlush(_encryptedOutput, pos);
|
||||||
if (wrapResult.bytesConsumed()>0)
|
if (wrapResult.bytesConsumed()>0)
|
||||||
{
|
|
||||||
consumed+=wrapResult.bytesConsumed();
|
consumed+=wrapResult.bytesConsumed();
|
||||||
|
|
||||||
// clear empty buffers to prevent position creeping up the buffer
|
boolean all_consumed=true;
|
||||||
for (ByteBuffer b : appOuts)
|
// clear empty buffers to prevent position creeping up the buffer
|
||||||
if (BufferUtil.isEmpty(b))
|
for (ByteBuffer b : appOuts)
|
||||||
BufferUtil.clear(b);
|
{
|
||||||
|
if (BufferUtil.isEmpty(b))
|
||||||
|
BufferUtil.clear(b);
|
||||||
|
else
|
||||||
|
all_consumed=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// and deal with the results returned from the sslEngineWrap
|
// and deal with the results returned from the sslEngineWrap
|
||||||
|
@ -669,15 +697,11 @@ public class SslConnection extends AbstractConnection
|
||||||
// the write has progressed normally and let a subsequent call to flush (or WriteFlusher#onIncompleteFlushed)
|
// the write has progressed normally and let a subsequent call to flush (or WriteFlusher#onIncompleteFlushed)
|
||||||
// to finish writing the close handshake. The caller will find out about the close on a subsequent flush or fill.
|
// to finish writing the close handshake. The caller will find out about the close on a subsequent flush or fill.
|
||||||
if (BufferUtil.hasContent(_encryptedOutput))
|
if (BufferUtil.hasContent(_encryptedOutput))
|
||||||
return consumed;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we were flushing because of a fill needing to wrap, return normally and it will handle the closed state.
|
|
||||||
if (appOuts[0]==__FILL_CALLED_FLUSH)
|
|
||||||
return consumed;
|
|
||||||
|
|
||||||
// otherwise we have written, and the caller will close the underlying connection
|
// otherwise we have written, and the caller will close the underlying connection
|
||||||
return consumed;
|
return all_consumed;
|
||||||
|
|
||||||
case BUFFER_UNDERFLOW:
|
case BUFFER_UNDERFLOW:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
@ -695,7 +719,7 @@ public class SslConnection extends AbstractConnection
|
||||||
{
|
{
|
||||||
case NOT_HANDSHAKING:
|
case NOT_HANDSHAKING:
|
||||||
// Return with the number of bytes consumed (which may be 0)
|
// Return with the number of bytes consumed (which may be 0)
|
||||||
return consumed;
|
return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
|
||||||
|
|
||||||
case NEED_TASK:
|
case NEED_TASK:
|
||||||
// run the task and continue
|
// run the task and continue
|
||||||
|
@ -715,7 +739,7 @@ public class SslConnection extends AbstractConnection
|
||||||
_flushRequiresFillToProgress = true;
|
_flushRequiresFillToProgress = true;
|
||||||
fill(__FLUSH_CALLED_FILL);
|
fill(__FLUSH_CALLED_FILL);
|
||||||
}
|
}
|
||||||
return consumed;
|
return all_consumed&&BufferUtil.isEmpty(_encryptedOutput);
|
||||||
|
|
||||||
case FINISHED:
|
case FINISHED:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
|
@ -115,23 +115,20 @@ public class ByteArrayEndPointTest
|
||||||
ByteArrayEndPoint endp = new ByteArrayEndPoint((byte[])null,15);
|
ByteArrayEndPoint endp = new ByteArrayEndPoint((byte[])null,15);
|
||||||
endp.setGrowOutput(true);
|
endp.setGrowOutput(true);
|
||||||
|
|
||||||
assertEquals(11,endp.flush(BufferUtil.toBuffer("some output")));
|
assertEquals(true,endp.flush(BufferUtil.toBuffer("some output")));
|
||||||
assertEquals("some output",endp.getOutputString());
|
assertEquals("some output",endp.getOutputString());
|
||||||
|
|
||||||
assertEquals(10,endp.flush(BufferUtil.toBuffer(" some more")));
|
assertEquals(true,endp.flush(BufferUtil.toBuffer(" some more")));
|
||||||
assertEquals("some output some more",endp.getOutputString());
|
assertEquals("some output some more",endp.getOutputString());
|
||||||
|
|
||||||
assertEquals(0,endp.flush());
|
assertEquals(true,endp.flush());
|
||||||
assertEquals("some output some more",endp.getOutputString());
|
assertEquals("some output some more",endp.getOutputString());
|
||||||
|
|
||||||
assertEquals(0,endp.flush(BufferUtil.EMPTY_BUFFER));
|
assertEquals(true,endp.flush(BufferUtil.EMPTY_BUFFER));
|
||||||
assertEquals("some output some more",endp.getOutputString());
|
assertEquals("some output some more",endp.getOutputString());
|
||||||
|
|
||||||
assertEquals(9,endp.flush(BufferUtil.EMPTY_BUFFER,BufferUtil.toBuffer(" and"),BufferUtil.toBuffer(" more")));
|
assertEquals(true,endp.flush(BufferUtil.EMPTY_BUFFER,BufferUtil.toBuffer(" and"),BufferUtil.toBuffer(" more")));
|
||||||
assertEquals("some output some more and more",endp.getOutputString());
|
assertEquals("some output some more and more",endp.getOutputString());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -142,13 +139,13 @@ public class ByteArrayEndPointTest
|
||||||
endp.setOutput(BufferUtil.allocate(10));
|
endp.setOutput(BufferUtil.allocate(10));
|
||||||
|
|
||||||
ByteBuffer data = BufferUtil.toBuffer("Some more data.");
|
ByteBuffer data = BufferUtil.toBuffer("Some more data.");
|
||||||
assertEquals(10,endp.flush(data));
|
assertEquals(false,endp.flush(data));
|
||||||
assertEquals("Some more ",endp.getOutputString());
|
assertEquals("Some more ",endp.getOutputString());
|
||||||
assertEquals("data.",BufferUtil.toString(data));
|
assertEquals("data.",BufferUtil.toString(data));
|
||||||
|
|
||||||
assertEquals("Some more ",endp.takeOutputString());
|
assertEquals("Some more ",endp.takeOutputString());
|
||||||
|
|
||||||
assertEquals(5,endp.flush(data));
|
assertEquals(true,endp.flush(data));
|
||||||
assertEquals("data.",BufferUtil.toString(endp.takeOutput()));
|
assertEquals("data.",BufferUtil.toString(endp.takeOutput()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,6 @@ public class SelectChannelEndPointTest
|
||||||
_endp.write(null, blockingWrite, out.asReadOnlyBuffer());
|
_endp.write(null, blockingWrite, out.asReadOnlyBuffer());
|
||||||
blockingWrite.get();
|
blockingWrite.get();
|
||||||
}
|
}
|
||||||
LOG.info("Finished writing {}", _writeCount);
|
|
||||||
progress = true;
|
progress = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -348,8 +348,10 @@ public class WriteFlusherTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConcurrentAccessToWriteAndOnFail() throws IOException, InterruptedException, ExecutionException
|
public void testConcurrentAccessToWriteAndOnFail() throws Exception
|
||||||
{
|
{
|
||||||
|
// TODO review this test - It was changed for the boolean flush return, but not really well inspected
|
||||||
|
|
||||||
final CountDownLatch failedCalledLatch = new CountDownLatch(1);
|
final CountDownLatch failedCalledLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch writeCalledLatch = new CountDownLatch(1);
|
final CountDownLatch writeCalledLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch writeCompleteLatch = new CountDownLatch(1);
|
final CountDownLatch writeCompleteLatch = new CountDownLatch(1);
|
||||||
|
@ -375,14 +377,28 @@ public class WriteFlusherTest
|
||||||
executor.submit(new Writer(writeFlusher, callback));
|
executor.submit(new Writer(writeFlusher, callback));
|
||||||
assertThat("Write has been called.", writeCalledLatch.await(5, TimeUnit.SECONDS), is(true));
|
assertThat("Write has been called.", writeCalledLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||||
executor.submit(new FailedCaller(writeFlusher, failedCalledLatch)).get();
|
executor.submit(new FailedCaller(writeFlusher, failedCalledLatch)).get();
|
||||||
|
|
||||||
|
|
||||||
// callback failed is NOT called because in WRITING state failed() doesn't know about the callback. However
|
// callback failed is NOT called because in WRITING state failed() doesn't know about the callback. However
|
||||||
// either the write succeeds or we get an IOException which will call callback.failed()
|
// either the write succeeds or we get an IOException which will call callback.failed()
|
||||||
assertThat("callback failed", callback.isFailed(), is(false));
|
|
||||||
assertThat("write complete", writeCompleteLatch.await(5, TimeUnit.SECONDS), is(true));
|
assertThat("write complete", writeCompleteLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||||
|
|
||||||
|
|
||||||
// in this testcase we more or less emulate that the write has successfully finished and we return from
|
// in this testcase we more or less emulate that the write has successfully finished and we return from
|
||||||
// EndPoint.flush() back to WriteFlusher.write(). Then someone calls failed. So the callback should have been
|
// EndPoint.flush() back to WriteFlusher.write(). Then someone calls failed. So the callback should have been
|
||||||
// completed.
|
// completed.
|
||||||
assertThat("callback completed", callback.isCompleted(), is(true));
|
try
|
||||||
|
{
|
||||||
|
callback.get(5,TimeUnit.SECONDS);
|
||||||
|
assertThat("callback completed", callback.isCompleted(), is(true));
|
||||||
|
assertThat("callback failed", callback.isFailed(), is(false));
|
||||||
|
}
|
||||||
|
catch(ExecutionException e)
|
||||||
|
{
|
||||||
|
// ignored because failure is expected
|
||||||
|
assertThat("callback failed", callback.isFailed(), is(true));
|
||||||
|
}
|
||||||
|
assertThat("callback completed", callback.isDone(), is(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ExposingStateCallback extends FutureCallback
|
private class ExposingStateCallback extends FutureCallback
|
||||||
|
@ -437,7 +453,7 @@ public class WriteFlusherTest
|
||||||
flushCalledLatch.countDown();
|
flushCalledLatch.countDown();
|
||||||
// make sure we stay here, so write is called twice at the same time
|
// make sure we stay here, so write is called twice at the same time
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
return null;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -528,7 +544,7 @@ public class WriteFlusherTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int flush(ByteBuffer... buffers) throws IOException
|
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||||
{
|
{
|
||||||
writeCalledLatch.countDown();
|
writeCalledLatch.countDown();
|
||||||
ByteBuffer byteBuffer = buffers[0];
|
ByteBuffer byteBuffer = buffers[0];
|
||||||
|
@ -549,13 +565,16 @@ public class WriteFlusherTest
|
||||||
else if (byteBuffer.remaining() == 3)
|
else if (byteBuffer.remaining() == 3)
|
||||||
{
|
{
|
||||||
byteBuffer.position(1); // pretend writing one byte
|
byteBuffer.position(1); // pretend writing one byte
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byteBuffer.position(byteBuffer.limit());
|
byteBuffer.position(byteBuffer.limit());
|
||||||
}
|
}
|
||||||
return byteBuffer.limit() - oldPos;
|
|
||||||
|
for (ByteBuffer b: buffers)
|
||||||
|
if (BufferUtil.hasContent(b))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,2 @@
|
||||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||||
org.eclipse.jetty.LEVEL=WARN
|
org.eclipse.jetty.LEVEL=INFO
|
||||||
# Enable the next two to debug SSL write blocked issue
|
|
||||||
org.eclipse.jetty.io.LEVEL=INFO
|
|
||||||
org.eclipse.jetty.io.SelectorManager.LEVEL=DEBUG
|
|
||||||
org.eclipse.jetty.io.SelectChannelEndPointTest.LEVEL=DEBUG
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
OPTIONS=plus
|
||||||
|
etc/jetty-plus.xml
|
|
@ -28,9 +28,10 @@ import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.PathMap;
|
import org.eclipse.jetty.http.PathMap;
|
||||||
import org.eclipse.jetty.server.HttpChannel;
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpChannelConfig;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Response;
|
import org.eclipse.jetty.server.Response;
|
||||||
import org.eclipse.jetty.server.UserIdentity;
|
import org.eclipse.jetty.server.UserIdentity;
|
||||||
|
@ -87,12 +88,14 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
/**
|
/**
|
||||||
* @return Returns the constraintMappings.
|
* @return Returns the constraintMappings.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public List<ConstraintMapping> getConstraintMappings()
|
public List<ConstraintMapping> getConstraintMappings()
|
||||||
{
|
{
|
||||||
return _constraintMappings;
|
return _constraintMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@Override
|
||||||
public Set<String> getRoles()
|
public Set<String> getRoles()
|
||||||
{
|
{
|
||||||
return _roles;
|
return _roles;
|
||||||
|
@ -134,6 +137,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
* The constraintMappings to set.
|
* The constraintMappings to set.
|
||||||
* @param roles The known roles (or null to determine them from the mappings)
|
* @param roles The known roles (or null to determine them from the mappings)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
|
public void setConstraintMappings(List<ConstraintMapping> constraintMappings, Set<String> roles)
|
||||||
{
|
{
|
||||||
if (isStarted())
|
if (isStarted())
|
||||||
|
@ -181,6 +185,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.security.ConstraintAware#addConstraintMapping(org.eclipse.jetty.security.ConstraintMapping)
|
* @see org.eclipse.jetty.security.ConstraintAware#addConstraintMapping(org.eclipse.jetty.security.ConstraintMapping)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void addConstraintMapping(ConstraintMapping mapping)
|
public void addConstraintMapping(ConstraintMapping mapping)
|
||||||
{
|
{
|
||||||
_constraintMappings.add(mapping);
|
_constraintMappings.add(mapping);
|
||||||
|
@ -198,6 +203,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
/**
|
/**
|
||||||
* @see org.eclipse.jetty.security.ConstraintAware#addRole(java.lang.String)
|
* @see org.eclipse.jetty.security.ConstraintAware#addRole(java.lang.String)
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void addRole(String role)
|
public void addRole(String role)
|
||||||
{
|
{
|
||||||
boolean modified = _roles.add(role);
|
boolean modified = _roles.add(role);
|
||||||
|
@ -325,6 +331,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
|
protected RoleInfo prepareConstraintInfo(String pathInContext, Request request)
|
||||||
{
|
{
|
||||||
Map<String, RoleInfo> mappings = _constraintMap.match(pathInContext);
|
Map<String, RoleInfo> mappings = _constraintMap.match(pathInContext);
|
||||||
|
@ -354,34 +361,16 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
|
if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
HttpConfiguration httpConfiguration = HttpChannel.getCurrentHttpChannel().getHttpConfiguration();
|
HttpChannelConfig httpConfig = HttpChannel.getCurrentHttpChannel().getHttpChannelConfig();
|
||||||
|
|
||||||
if (dataConstraint == UserDataConstraint.Integral)
|
if (dataConstraint == UserDataConstraint.Confidential || dataConstraint == UserDataConstraint.Integral)
|
||||||
{
|
{
|
||||||
if (httpConfiguration.isIntegral(request))
|
if (request.isSecure())
|
||||||
return true;
|
|
||||||
if (httpConfiguration.getIntegralPort() > 0)
|
|
||||||
{
|
|
||||||
String url = httpConfiguration.getIntegralScheme() + "://" + request.getServerName() + ":" + httpConfiguration.getIntegralPort() + request.getRequestURI();
|
|
||||||
if (request.getQueryString() != null)
|
|
||||||
url += "?" + request.getQueryString();
|
|
||||||
response.setContentLength(0);
|
|
||||||
response.sendRedirect(url);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
response.sendError(Response.SC_FORBIDDEN,"!Integral");
|
|
||||||
|
|
||||||
request.setHandled(true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (dataConstraint == UserDataConstraint.Confidential)
|
|
||||||
{
|
|
||||||
if (httpConfiguration.isConfidential(request))
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (httpConfiguration.getConfidentialPort() > 0)
|
if (httpConfig.getSecurePort() > 0)
|
||||||
{
|
{
|
||||||
String url = httpConfiguration.getConfidentialScheme() + "://" + request.getServerName() + ":" + httpConfiguration.getConfidentialPort()
|
String url = httpConfig.getSecureScheme() + "://" + request.getServerName() + ":" + httpConfig.getSecurePort()
|
||||||
+ request.getRequestURI();
|
+ request.getRequestURI();
|
||||||
if (request.getQueryString() != null)
|
if (request.getQueryString() != null)
|
||||||
url += "?" + request.getQueryString();
|
url += "?" + request.getQueryString();
|
||||||
|
@ -390,7 +379,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
response.sendRedirect(url);
|
response.sendRedirect(url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
response.sendError(Response.SC_FORBIDDEN,"!Confidential");
|
response.sendError(HttpStatus.FORBIDDEN_403,"!Secure");
|
||||||
|
|
||||||
request.setHandled(true);
|
request.setHandled(true);
|
||||||
return false;
|
return false;
|
||||||
|
@ -402,6 +391,7 @@ public class ConstraintSecurityHandler extends SecurityHandler implements Constr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
|
protected boolean isAuthMandatory(Request baseRequest, Response base_response, Object constraintInfo)
|
||||||
{
|
{
|
||||||
return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
|
return constraintInfo != null && ((RoleInfo)constraintInfo).isChecked();
|
||||||
|
|
|
@ -28,8 +28,8 @@ import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
import org.eclipse.jetty.security.authentication.BasicAuthenticator;
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.server.HttpConfiguration;
|
import org.eclipse.jetty.server.HttpChannelConfig;
|
||||||
import org.eclipse.jetty.server.HttpServerConnectionFactory;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.LocalConnector;
|
import org.eclipse.jetty.server.LocalConnector;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
@ -60,38 +60,25 @@ public class DataConstraintsTest
|
||||||
public void startServer()
|
public void startServer()
|
||||||
{
|
{
|
||||||
_server = new Server();
|
_server = new Server();
|
||||||
_connector = new LocalConnector(_server);
|
|
||||||
|
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||||
|
http.getHttpChannelConfig().setSecurePort(9999);
|
||||||
|
http.getHttpChannelConfig().setSecureScheme("BWTP");
|
||||||
|
_connector = new LocalConnector(_server,http);
|
||||||
_connector.setIdleTimeout(300000);
|
_connector.setIdleTimeout(300000);
|
||||||
HttpConfiguration httpConfiguration = new HttpConfiguration(null, false);
|
|
||||||
httpConfiguration.setIntegralPort(9998);
|
|
||||||
httpConfiguration.setIntegralScheme("FTP");
|
|
||||||
httpConfiguration.setConfidentialPort(9999);
|
|
||||||
httpConfiguration.setConfidentialScheme("SPDY");
|
|
||||||
_connector.setDefaultConnectionFactory(new HttpServerConnectionFactory(_connector, httpConfiguration));
|
|
||||||
|
|
||||||
_connectorS = new LocalConnector(_server);
|
HttpConnectionFactory https = new HttpConnectionFactory();
|
||||||
_connectorS.setDefaultConnectionFactory(new HttpServerConnectionFactory(_connectorS, new HttpConfiguration(null,false)
|
https.getHttpChannelConfig().addCustomizer(new HttpChannelConfig.Customizer()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void customize(Request request) throws IOException
|
public void customize(Connector connector, HttpChannelConfig channelConfig, Request request)
|
||||||
{
|
{
|
||||||
request.setScheme(HttpScheme.HTTPS.asString());
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
super.customize(request);
|
request.setSecure(true);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
_connectorS = new LocalConnector(_server,https);
|
||||||
public boolean isIntegral(Request request)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConfidential(Request request)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
_server.setConnectors(new Connector[]{_connector,_connectorS});
|
_server.setConnectors(new Connector[]{_connector,_connectorS});
|
||||||
|
|
||||||
ContextHandler _context = new ContextHandler();
|
ContextHandler _context = new ContextHandler();
|
||||||
|
@ -106,6 +93,7 @@ public class DataConstraintsTest
|
||||||
|
|
||||||
_security.setHandler(new AbstractHandler()
|
_security.setHandler(new AbstractHandler()
|
||||||
{
|
{
|
||||||
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
@ -149,8 +137,8 @@ public class DataConstraintsTest
|
||||||
|
|
||||||
response = _connector.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response, containsString("HTTP/1.1 302 Found"));
|
assertThat(response, containsString("HTTP/1.1 302 Found"));
|
||||||
assertThat(response, containsString("Location: FTP://"));
|
assertThat(response, containsString("Location: BWTP://"));
|
||||||
assertThat(response, containsString(":9998"));
|
assertThat(response, containsString(":9999"));
|
||||||
|
|
||||||
response = _connectorS.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n");
|
response = _connectorS.getResponses("GET /ctx/integral/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response, containsString("HTTP/1.1 404 Not Found"));
|
assertThat(response, containsString("HTTP/1.1 404 Not Found"));
|
||||||
|
@ -181,7 +169,7 @@ public class DataConstraintsTest
|
||||||
|
|
||||||
response = _connector.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n");
|
response = _connector.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n");
|
||||||
assertThat(response, containsString("HTTP/1.1 302 Found"));
|
assertThat(response, containsString("HTTP/1.1 302 Found"));
|
||||||
assertThat(response, containsString("Location: SPDY://"));
|
assertThat(response, containsString("Location: BWTP://"));
|
||||||
assertThat(response, containsString(":9999"));
|
assertThat(response, containsString(":9999"));
|
||||||
|
|
||||||
response = _connectorS.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n");
|
response = _connectorS.getResponses("GET /ctx/confid/info HTTP/1.0\r\n\r\n");
|
||||||
|
@ -444,11 +432,14 @@ public class DataConstraintsTest
|
||||||
{
|
{
|
||||||
this.identityService = identityService;
|
this.identityService = identityService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return "name";
|
return "name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public UserIdentity login(String username, Object credentials)
|
public UserIdentity login(String username, Object credentials)
|
||||||
{
|
{
|
||||||
if("admin".equals(username) && "password".equals(credentials))
|
if("admin".equals(username) && "password".equals(credentials))
|
||||||
|
@ -456,20 +447,24 @@ public class DataConstraintsTest
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean validate(UserIdentity user)
|
public boolean validate(UserIdentity user)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public IdentityService getIdentityService()
|
public IdentityService getIdentityService()
|
||||||
{
|
{
|
||||||
return identityService;
|
return identityService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setIdentityService(IdentityService service)
|
public void setIdentityService(IdentityService service)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void logout(UserIdentity user)
|
public void logout(UserIdentity user)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,12 @@
|
||||||
<artifactId>jetty-http</artifactId>
|
<artifactId>jetty-http</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-xml</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-jmx</artifactId>
|
<artifactId>jetty-jmx</artifactId>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
|
||||||
|
|
||||||
<!-- =============================================================== -->
|
<!-- =============================================================== -->
|
||||||
<!-- Configure SSL for the Jetty Server -->
|
<!-- Configure SSL for the Jetty Server -->
|
||||||
|
@ -11,25 +11,52 @@
|
||||||
<!-- =============================================================== -->
|
<!-- =============================================================== -->
|
||||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||||
|
|
||||||
<!-- if NIO is not available, use org.eclipse.jetty.server.ssl.SslSocketConnector -->
|
<!-- =========================================================== -->
|
||||||
|
<!-- Setup a SSL Context factory -->
|
||||||
<New id="sslContextFactory" class="org.eclipse.jetty.http.ssl.SslContextFactory">
|
<!-- =========================================================== -->
|
||||||
<Set name="KeyStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
<New id="sslContextFactory" class="org.eclipse.jetty.util.ssl.SslContextFactory">
|
||||||
|
<Set name="KeyStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||||
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
<Set name="KeyStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||||
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
<Set name="KeyManagerPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
|
||||||
<Set name="TrustStore"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
|
||||||
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
|
||||||
</New>
|
</New>
|
||||||
|
|
||||||
|
<!-- =========================================================== -->
|
||||||
|
<!-- Add HTTP Customizer for Secure request -->
|
||||||
|
<!-- =========================================================== -->
|
||||||
|
<Ref id="httpConfig">
|
||||||
|
<Call name="addCustomizer">
|
||||||
|
<Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
|
||||||
|
</Call>
|
||||||
|
</Ref>
|
||||||
|
|
||||||
<Call name="addConnector">
|
<!-- =========================================================== -->
|
||||||
|
<!-- Set connectors -->
|
||||||
|
<!-- =========================================================== -->
|
||||||
|
<Call id="sslConnector" name="addConnector">
|
||||||
<Arg>
|
<Arg>
|
||||||
<New class="org.eclipse.jetty.server.ssl.SslSelectChannelConnector">
|
<New class="org.eclipse.jetty.server.SelectChannelConnector">
|
||||||
<Arg><Ref id="sslContextFactory" /></Arg>
|
<Arg name="server"><Ref id="Server" /></Arg>
|
||||||
<Set name="Port">8443</Set>
|
<Arg name="factories">
|
||||||
<Set name="idleTimeout">30000</Set>
|
<Array type="org.eclipse.jetty.server.ConnectionFactory">
|
||||||
<Set name="Acceptors">2</Set>
|
<Item>
|
||||||
<Set name="AcceptQueueSize">100</Set>
|
<New class="org.eclipse.jetty.server.SslConnectionFactory">
|
||||||
</New>
|
<Arg name="next">http/1.1</Arg>
|
||||||
|
<Arg name="sslContextFactory"><Ref id="sslContextFactory"/></Arg>
|
||||||
|
</New>
|
||||||
|
</Item>
|
||||||
|
<Item>
|
||||||
|
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
|
||||||
|
<Arg name="config"><Ref id="httpConfig"/></Arg>
|
||||||
|
</New>
|
||||||
|
</Item>
|
||||||
|
</Array>
|
||||||
|
</Arg>
|
||||||
|
<Set name="host"><Property name="jetty.host" /></Set>
|
||||||
|
<Set name="port">8443</Set>
|
||||||
|
<Set name="idleTimeout">30000</Set>
|
||||||
|
</New>
|
||||||
</Arg>
|
</Arg>
|
||||||
</Call>
|
</Call>
|
||||||
</Configure>
|
</Configure>
|
||||||
|
|
|
@ -29,6 +29,21 @@
|
||||||
</Arg>
|
</Arg>
|
||||||
<Arg name="container"><Ref id="Container"/></Arg>
|
<Arg name="container"><Ref id="Container"/></Arg>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- =========================================================== -->
|
||||||
|
<!-- HttpChannel Configuration -->
|
||||||
|
<!-- =========================================================== -->
|
||||||
|
<New id="httpConfig" class="org.eclipse.jetty.server.HttpChannelConfig">
|
||||||
|
<Set name="secureScheme">https</Set>
|
||||||
|
<Set name="securePort">8443</Set>
|
||||||
|
<Set name="outputBufferSize">32768</Set>
|
||||||
|
<Set name="requestHeaderSize">8192</Set>
|
||||||
|
<Set name="responseHeaderSize">8192</Set>
|
||||||
|
<Call name="addCustomizer">
|
||||||
|
<Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
|
||||||
|
</Call>
|
||||||
|
</New>
|
||||||
|
|
||||||
<!-- =========================================================== -->
|
<!-- =========================================================== -->
|
||||||
<!-- Set connectors -->
|
<!-- Set connectors -->
|
||||||
<!-- =========================================================== -->
|
<!-- =========================================================== -->
|
||||||
|
@ -36,9 +51,16 @@
|
||||||
<Arg>
|
<Arg>
|
||||||
<New class="org.eclipse.jetty.server.SelectChannelConnector">
|
<New class="org.eclipse.jetty.server.SelectChannelConnector">
|
||||||
<Arg name="server"><Ref id="Server" /></Arg>
|
<Arg name="server"><Ref id="Server" /></Arg>
|
||||||
|
<Arg name="factories">
|
||||||
|
<Array type="org.eclipse.jetty.server.ConnectionFactory">
|
||||||
|
<Item><New class="org.eclipse.jetty.server.HttpConnectionFactory">
|
||||||
|
<Arg name="config"><Ref id="httpConfig"/></Arg>
|
||||||
|
</New></Item>
|
||||||
|
</Array>
|
||||||
|
</Arg>
|
||||||
<Set name="host"><Property name="jetty.host" /></Set>
|
<Set name="host"><Property name="jetty.host" /></Set>
|
||||||
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
|
<Set name="port"><Property name="jetty.port" default="8080"/></Set>
|
||||||
<Set name="idleTimeout">300000</Set>
|
<Set name="idleTimeout">30000</Set>
|
||||||
</New>
|
</New>
|
||||||
</Arg>
|
</Arg>
|
||||||
</Call>
|
</Call>
|
||||||
|
@ -68,7 +90,7 @@
|
||||||
<Set name="sendServerVersion">true</Set>
|
<Set name="sendServerVersion">true</Set>
|
||||||
<Set name="sendDateHeader">true</Set>
|
<Set name="sendDateHeader">true</Set>
|
||||||
<Set name="stopTimeout">1000</Set>
|
<Set name="stopTimeout">1000</Set>
|
||||||
<Set name="dumpAfterStart">false</Set>
|
<Set name="dumpAfterStart">true</Set>
|
||||||
<Set name="dumpBeforeStop">false</Set>
|
<Set name="dumpBeforeStop">false</Set>
|
||||||
|
|
||||||
</Configure>
|
</Configure>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.ArrayUtil;
|
||||||
|
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
||||||
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
|
||||||
|
public abstract class AbstractConnectionFactory extends AggregateLifeCycle implements ConnectionFactory
|
||||||
|
{
|
||||||
|
final String _protocol;
|
||||||
|
int _inputbufferSize=8192;
|
||||||
|
|
||||||
|
protected AbstractConnectionFactory(String protocol)
|
||||||
|
{
|
||||||
|
_protocol=protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProtocol()
|
||||||
|
{
|
||||||
|
return _protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getInputBufferSize()
|
||||||
|
{
|
||||||
|
return _inputbufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInputBufferSize(int size)
|
||||||
|
{
|
||||||
|
_inputbufferSize=size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),getProtocol());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConnectionFactory[] getFactories(SslContextFactory sslContextFactory, ConnectionFactory... factories)
|
||||||
|
{
|
||||||
|
factories=ArrayUtil.removeNulls(factories);
|
||||||
|
|
||||||
|
if (sslContextFactory==null)
|
||||||
|
return factories;
|
||||||
|
|
||||||
|
for (ConnectionFactory factory : factories)
|
||||||
|
{
|
||||||
|
if (factory instanceof HttpChannelConfig.ConnectionFactory)
|
||||||
|
{
|
||||||
|
HttpChannelConfig config = ((HttpChannelConfig.ConnectionFactory)factory).getHttpChannelConfig();
|
||||||
|
if (config.getCustomizer(SecureRequestCustomizer.class)==null)
|
||||||
|
config.addCustomizer(new SecureRequestCustomizer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ArrayUtil.prependToArray(new SslConnectionFactory(sslContextFactory,factories[0].getProtocol()),factories,ConnectionFactory.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -20,7 +20,10 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
@ -37,51 +40,63 @@ import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
||||||
import org.eclipse.jetty.util.component.Dumpable;
|
import org.eclipse.jetty.util.component.Dumpable;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
import org.eclipse.jetty.util.thread.TimerScheduler;
|
import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Partial implementation of {@link Connector}</p>
|
* <p>Partial implementation of {@link Connector}</p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The connector keeps a collection of {@link ConnectionFactory} instances, each of which are known by their
|
||||||
|
* protocol name. The protocol name may be a real protocol (eg http/1.1 or spdy/3) or it may be a private name
|
||||||
|
* that represents a special connection factory. For example, the name "SSL-http/1.1" is used for
|
||||||
|
* an {@link SslConnectionFactory} that has been instantiated with the {@link HttpConnectionFactory} as it's
|
||||||
|
* next protocol.
|
||||||
|
* <p>
|
||||||
|
* If NPN is used to select the real protocol used by an SSL connection, then the name "SSL-NPN" is used,
|
||||||
|
* which represents a {@link SslConnectionFactory} with a NPNConnectionFactory as the next protocol. Once
|
||||||
|
* the NPN connection is established, it will get the next protocol from the NPN extension and then call
|
||||||
|
* {@link #getConnectionFactory(String)} to get the next connection factory.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
@ManagedObject("Abstract implementation of the Connector Interface")
|
@ManagedObject("Abstract implementation of the Connector Interface")
|
||||||
public abstract class AbstractConnector extends AggregateLifeCycle implements Connector, Dumpable
|
public abstract class AbstractConnector extends AggregateLifeCycle implements Connector, Dumpable
|
||||||
{
|
{
|
||||||
protected final Logger LOG = Log.getLogger(getClass());
|
protected final Logger LOG = Log.getLogger(getClass());
|
||||||
// Order is important on server side, so we use a LinkedHashMap
|
// Order is important on server side, so we use a LinkedHashMap
|
||||||
private final Map<String, ConnectionFactory> factories = new LinkedHashMap<>();
|
private final Map<String, ConnectionFactory> _factories = new LinkedHashMap<>();
|
||||||
private final Statistics _stats = new ConnectorStatistics();
|
private final Statistics _stats = new ConnectorStatistics();
|
||||||
private final Server _server;
|
private final Server _server;
|
||||||
private final SslContextFactory _sslContextFactory;
|
|
||||||
private final Executor _executor;
|
private final Executor _executor;
|
||||||
private final Scheduler _scheduler;
|
private final Scheduler _scheduler;
|
||||||
private final ByteBufferPool _byteBufferPool;
|
private final ByteBufferPool _byteBufferPool;
|
||||||
private final Thread[] _acceptors;
|
private final Thread[] _acceptors;
|
||||||
private volatile CountDownLatch _stopping;
|
private volatile CountDownLatch _stopping;
|
||||||
private volatile long _idleTimeout = 200000;
|
private long _idleTimeout = 200000;
|
||||||
private volatile ConnectionFactory defaultConnectionFactory;
|
private String _defaultProtocol;
|
||||||
|
private ConnectionFactory _defaultConnectionFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param server The server this connector will be added to. Must not be null.
|
* @param server The server this connector will be added to. Must not be null.
|
||||||
|
* @param factory TODO
|
||||||
|
* @param sslContextFactory the SSL context factory to make this connector SSL enabled, or null
|
||||||
* @param executor An executor for this connector or null to use the servers executor
|
* @param executor An executor for this connector or null to use the servers executor
|
||||||
* @param scheduler A scheduler for this connector or null to use the servers scheduler
|
* @param scheduler A scheduler for this connector or null to use the servers scheduler
|
||||||
* @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool}
|
* @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool}
|
||||||
* @param sslContextFactory the SSL context factory to make this connector SSL enabled, or null
|
|
||||||
* @param acceptors the number of acceptor threads to use, or 0 for a default value.
|
* @param acceptors the number of acceptor threads to use, or 0 for a default value.
|
||||||
*/
|
*/
|
||||||
public AbstractConnector(
|
public AbstractConnector(
|
||||||
Server server,
|
Server server,
|
||||||
Executor executor,
|
Executor executor,
|
||||||
Scheduler scheduler,
|
Scheduler scheduler,
|
||||||
ByteBufferPool pool,
|
ByteBufferPool pool,
|
||||||
SslContextFactory sslContextFactory,
|
int acceptors,
|
||||||
int acceptors)
|
ConnectionFactory... factories)
|
||||||
{
|
{
|
||||||
_server=server;
|
_server=server;
|
||||||
_executor=executor!=null?executor:_server.getThreadPool();
|
_executor=executor!=null?executor:_server.getThreadPool();
|
||||||
_scheduler=scheduler!=null?scheduler:new TimerScheduler();
|
_scheduler=scheduler!=null?scheduler:new TimerScheduler();
|
||||||
_byteBufferPool = pool!=null?pool:new ArrayByteBufferPool();
|
_byteBufferPool = pool!=null?pool:new ArrayByteBufferPool();
|
||||||
_sslContextFactory = sslContextFactory;
|
|
||||||
|
|
||||||
addBean(_server,false);
|
addBean(_server,false);
|
||||||
addBean(_executor);
|
addBean(_executor);
|
||||||
|
@ -89,9 +104,11 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co
|
||||||
unmanage(_executor); // inherited from server
|
unmanage(_executor); // inherited from server
|
||||||
addBean(_scheduler);
|
addBean(_scheduler);
|
||||||
addBean(_byteBufferPool);
|
addBean(_byteBufferPool);
|
||||||
addBean(_sslContextFactory);
|
|
||||||
addBean(_stats,true);
|
addBean(_stats,true);
|
||||||
|
|
||||||
|
for (ConnectionFactory factory:factories)
|
||||||
|
addConnectionFactory(factory);
|
||||||
|
|
||||||
if (acceptors<=0)
|
if (acceptors<=0)
|
||||||
acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 4);
|
acceptors=Math.max(1,(Runtime.getRuntime().availableProcessors()) / 4);
|
||||||
if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
|
if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
|
||||||
|
@ -123,12 +140,6 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co
|
||||||
return _byteBufferPool;
|
return _byteBufferPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SslContextFactory getSslContextFactory()
|
|
||||||
{
|
|
||||||
return _sslContextFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getIdleTimeout()
|
public long getIdleTimeout()
|
||||||
{
|
{
|
||||||
|
@ -162,10 +173,13 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co
|
||||||
return _acceptors.length;
|
return _acceptors.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
{
|
{
|
||||||
|
_defaultConnectionFactory = getConnectionFactory(_defaultProtocol);
|
||||||
|
if(_defaultConnectionFactory==null)
|
||||||
|
throw new IllegalStateException("No protocol factory for default protocol: "+_defaultProtocol);
|
||||||
|
|
||||||
super.doStart();
|
super.doStart();
|
||||||
|
|
||||||
_stopping=new CountDownLatch(_acceptors.length);
|
_stopping=new CountDownLatch(_acceptors.length);
|
||||||
|
@ -232,56 +246,97 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co
|
||||||
return isRunning();
|
return isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConnectionFactory getConnectionFactory(String protocol)
|
public ConnectionFactory getConnectionFactory(String protocol)
|
||||||
{
|
{
|
||||||
synchronized (factories)
|
synchronized (_factories)
|
||||||
{
|
{
|
||||||
return factories.get(protocol);
|
return _factories.get(protocol.toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionFactory putConnectionFactory(String protocol, ConnectionFactory factory)
|
@Override
|
||||||
|
public <T extends ConnectionFactory> T getConnectionFactory(Class<T> factoryType)
|
||||||
{
|
{
|
||||||
synchronized (factories)
|
synchronized (_factories)
|
||||||
{
|
{
|
||||||
return factories.put(protocol, factory);
|
for (ConnectionFactory f : _factories.values())
|
||||||
|
if (factoryType.isAssignableFrom(f.getClass()))
|
||||||
|
return (T)f;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addConnectionFactory(ConnectionFactory factory)
|
||||||
|
{
|
||||||
|
synchronized (_factories)
|
||||||
|
{
|
||||||
|
ConnectionFactory old=_factories.remove(factory.getProtocol());
|
||||||
|
if (old!=null)
|
||||||
|
removeBean(old);
|
||||||
|
_factories.put(factory.getProtocol().toLowerCase(), factory);
|
||||||
|
addBean(factory);
|
||||||
|
if (_defaultProtocol==null)
|
||||||
|
_defaultProtocol=factory.getProtocol();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConnectionFactory removeConnectionFactory(String protocol)
|
public ConnectionFactory removeConnectionFactory(String protocol)
|
||||||
{
|
{
|
||||||
synchronized (factories)
|
synchronized (_factories)
|
||||||
{
|
{
|
||||||
return factories.remove(protocol);
|
ConnectionFactory factory= _factories.remove(protocol.toLowerCase());
|
||||||
|
removeBean(factory);
|
||||||
|
return factory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, ConnectionFactory> getConnectionFactories()
|
@Override
|
||||||
|
public Collection<ConnectionFactory> getConnectionFactories()
|
||||||
{
|
{
|
||||||
synchronized (factories)
|
synchronized (_factories)
|
||||||
{
|
{
|
||||||
return new LinkedHashMap<>(factories);
|
return _factories.values();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getProtocols()
|
||||||
|
{
|
||||||
|
synchronized (_factories)
|
||||||
|
{
|
||||||
|
return new ArrayList<>(_factories.keySet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearConnectionFactories()
|
public void clearConnectionFactories()
|
||||||
{
|
{
|
||||||
synchronized (factories)
|
synchronized (_factories)
|
||||||
{
|
{
|
||||||
factories.clear();
|
_factories.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDefaultProtocol()
|
||||||
|
{
|
||||||
|
return _defaultProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultProtocol(String defaultProtocol)
|
||||||
|
{
|
||||||
|
_defaultProtocol = defaultProtocol.toLowerCase();
|
||||||
|
if (isRunning())
|
||||||
|
_defaultConnectionFactory=getConnectionFactory(_defaultProtocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ConnectionFactory getDefaultConnectionFactory()
|
public ConnectionFactory getDefaultConnectionFactory()
|
||||||
{
|
{
|
||||||
return defaultConnectionFactory;
|
if (isStarted())
|
||||||
|
return _defaultConnectionFactory;
|
||||||
|
return getConnectionFactory(_defaultProtocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultConnectionFactory(ConnectionFactory defaultConnectionFactory)
|
|
||||||
{
|
|
||||||
this.defaultConnectionFactory = defaultConnectionFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Acceptor implements Runnable
|
private class Acceptor implements Runnable
|
||||||
{
|
{
|
||||||
private final int _acceptor;
|
private final int _acceptor;
|
||||||
|
@ -358,4 +413,13 @@ public abstract class AbstractConnector extends AggregateLifeCycle implements Co
|
||||||
{
|
{
|
||||||
return _scheduler;
|
return _scheduler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x{%s}",
|
||||||
|
getClass().getSimpleName(),
|
||||||
|
hashCode(),
|
||||||
|
getDefaultProtocol());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,9 +33,9 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme
|
||||||
private volatile String _host;
|
private volatile String _host;
|
||||||
private volatile int _port = 0;
|
private volatile int _port = 0;
|
||||||
|
|
||||||
public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, SslContextFactory sslContextFactory, int acceptors)
|
public AbstractNetworkConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories)
|
||||||
{
|
{
|
||||||
super(server, executor, scheduler, pool, sslContextFactory, acceptors);
|
super(server,executor,scheduler,pool,acceptors,factories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHost(String host)
|
public void setHost(String host)
|
||||||
|
@ -110,8 +109,8 @@ public abstract class AbstractNetworkConnector extends AbstractConnector impleme
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s@%s:%d",
|
return String.format("%s{%s:%d}",
|
||||||
getClass().getSimpleName(),
|
super.toString(),
|
||||||
getHost() == null ? "0.0.0.0" : getHost(),
|
getHost() == null ? "0.0.0.0" : getHost(),
|
||||||
getLocalPort() <= 0 ? getPort() : getLocalPort());
|
getLocalPort() <= 0 ? getPort() : getLocalPort());
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,5 @@ public class ByteBufferHttpInput extends HttpInput<ByteBuffer>
|
||||||
@Override
|
@Override
|
||||||
protected void onContentConsumed(ByteBuffer item)
|
protected void onContentConsumed(ByteBuffer item)
|
||||||
{
|
{
|
||||||
item.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
@ -28,12 +27,14 @@ import org.eclipse.jetty.io.EndPoint;
|
||||||
*/
|
*/
|
||||||
public interface ConnectionFactory
|
public interface ConnectionFactory
|
||||||
{
|
{
|
||||||
|
public String getProtocol();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Creates a new {@link Connection} with the given parameters</p>
|
* <p>Creates a new {@link Connection} with the given parameters</p>
|
||||||
* @param channel the {@link SocketChannel} associated with the connection
|
* @param connector The {@link Connector} creating this connection
|
||||||
* @param endPoint the {@link EndPoint} associated with the connection
|
* @param endPoint the {@link EndPoint} associated with the connection
|
||||||
* @param attachment the attachment associated with the connection
|
|
||||||
* @return a new {@link Connection}
|
* @return a new {@link Connection}
|
||||||
*/
|
*/
|
||||||
public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment);
|
public Connection newConnection(Connector connector, EndPoint endPoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
|
@ -25,7 +27,6 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.component.Graceful;
|
import org.eclipse.jetty.util.component.Graceful;
|
||||||
import org.eclipse.jetty.util.component.LifeCycle;
|
import org.eclipse.jetty.util.component.LifeCycle;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,10 +58,22 @@ public interface Connector extends LifeCycle, Graceful
|
||||||
public ByteBufferPool getByteBufferPool();
|
public ByteBufferPool getByteBufferPool();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the {@link SslContextFactory} associated with this {@link Connector}
|
* @return the {@link ConnectionFactory} associated with the protocol name
|
||||||
*/
|
*/
|
||||||
public SslContextFactory getSslContextFactory();
|
public ConnectionFactory getConnectionFactory(String nextProtocol);
|
||||||
|
|
||||||
|
|
||||||
|
public <T extends ConnectionFactory> T getConnectionFactory(Class<T> factoryType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the default {@link ConnectionFactory} associated with the default protocol name
|
||||||
|
*/
|
||||||
|
public ConnectionFactory getDefaultConnectionFactory();
|
||||||
|
|
||||||
|
public Collection<ConnectionFactory> getConnectionFactories();
|
||||||
|
|
||||||
|
public List<String> getProtocols();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the dle timeout for connections in milliseconds
|
* @return the dle timeout for connections in milliseconds
|
||||||
*/
|
*/
|
||||||
|
@ -199,4 +212,6 @@ public interface Connector extends LifeCycle, Graceful
|
||||||
*/
|
*/
|
||||||
public void connectionClosed(long duration, int messagesIn, int messagesOut);
|
public void connectionClosed(long duration, int messagesIn, int messagesOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.server.HttpChannelConfig.Customizer;
|
||||||
|
|
||||||
|
public class ForwardedRequestCustomizer implements Customizer
|
||||||
|
{
|
||||||
|
private String _hostHeader;
|
||||||
|
private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
|
||||||
|
private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
|
||||||
|
private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
|
||||||
|
private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
|
||||||
|
private String _forwardedCipherSuiteHeader;
|
||||||
|
private String _forwardedSslSessionIdHeader;
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
public String getHostHeader()
|
||||||
|
{
|
||||||
|
return _hostHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
|
||||||
|
* This value is only used if {@link #isForwarded()} is true.
|
||||||
|
*
|
||||||
|
* @param hostHeader
|
||||||
|
* The value of the host header to force.
|
||||||
|
*/
|
||||||
|
public void setHostHeader(String hostHeader)
|
||||||
|
{
|
||||||
|
_hostHeader = hostHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public String getForwardedHostHeader()
|
||||||
|
{
|
||||||
|
return _forwardedHostHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @param forwardedHostHeader
|
||||||
|
* The header name for forwarded hosts (default x-forwarded-host)
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public void setForwardedHostHeader(String forwardedHostHeader)
|
||||||
|
{
|
||||||
|
_forwardedHostHeader = forwardedHostHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @return the header name for forwarded server.
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public String getForwardedServerHeader()
|
||||||
|
{
|
||||||
|
return _forwardedServerHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @param forwardedServerHeader
|
||||||
|
* The header name for forwarded server (default x-forwarded-server)
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public void setForwardedServerHeader(String forwardedServerHeader)
|
||||||
|
{
|
||||||
|
_forwardedServerHeader = forwardedServerHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @return the forwarded for header
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public String getForwardedForHeader()
|
||||||
|
{
|
||||||
|
return _forwardedForHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @param forwardedRemoteAddressHeader
|
||||||
|
* The header name for forwarded for (default x-forwarded-for)
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public void setForwardedForHeader(String forwardedRemoteAddressHeader)
|
||||||
|
{
|
||||||
|
_forwardedForHeader = forwardedRemoteAddressHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* Get the forwardedProtoHeader.
|
||||||
|
*
|
||||||
|
* @return the forwardedProtoHeader (default X-Forwarded-For)
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public String getForwardedProtoHeader()
|
||||||
|
{
|
||||||
|
return _forwardedProtoHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* Set the forwardedProtoHeader.
|
||||||
|
*
|
||||||
|
* @param forwardedProtoHeader
|
||||||
|
* the forwardedProtoHeader to set (default X-Forwarded-For)
|
||||||
|
* @see #setForwarded(boolean)
|
||||||
|
*/
|
||||||
|
public void setForwardedProtoHeader(String forwardedProtoHeader)
|
||||||
|
{
|
||||||
|
_forwardedProtoHeader = forwardedProtoHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @return The header name holding a forwarded cipher suite (default null)
|
||||||
|
*/
|
||||||
|
public String getForwardedCipherSuiteHeader()
|
||||||
|
{
|
||||||
|
return _forwardedCipherSuiteHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @param forwardedCipherSuite
|
||||||
|
* The header name holding a forwarded cipher suite (default null)
|
||||||
|
*/
|
||||||
|
public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
|
||||||
|
{
|
||||||
|
_forwardedCipherSuiteHeader = forwardedCipherSuite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @return The header name holding a forwarded SSL Session ID (default null)
|
||||||
|
*/
|
||||||
|
public String getForwardedSslSessionIdHeader()
|
||||||
|
{
|
||||||
|
return _forwardedSslSessionIdHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* @param forwardedSslSessionId
|
||||||
|
* The header name holding a forwarded SSL Session ID (default null)
|
||||||
|
*/
|
||||||
|
public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
|
||||||
|
{
|
||||||
|
_forwardedSslSessionIdHeader = forwardedSslSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
@Override
|
||||||
|
public void customize(Connector connector, HttpChannelConfig config, Request request)
|
||||||
|
{
|
||||||
|
HttpFields httpFields = request.getHttpFields();
|
||||||
|
|
||||||
|
// Do SSL first
|
||||||
|
if (getForwardedCipherSuiteHeader()!=null)
|
||||||
|
{
|
||||||
|
String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
|
||||||
|
if (cipher_suite!=null)
|
||||||
|
request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
|
||||||
|
}
|
||||||
|
if (getForwardedSslSessionIdHeader()!=null)
|
||||||
|
{
|
||||||
|
String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
|
||||||
|
if(ssl_session_id!=null)
|
||||||
|
{
|
||||||
|
request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
|
||||||
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieving headers from the request
|
||||||
|
String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
|
||||||
|
String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
|
||||||
|
String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
|
||||||
|
String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
|
||||||
|
|
||||||
|
if (_hostHeader != null)
|
||||||
|
{
|
||||||
|
// Update host header
|
||||||
|
httpFields.put(HttpHeader.HOST.toString(),_hostHeader);
|
||||||
|
request.setServerName(null);
|
||||||
|
request.setServerPort(-1);
|
||||||
|
request.getServerName();
|
||||||
|
}
|
||||||
|
else if (forwardedHost != null)
|
||||||
|
{
|
||||||
|
// Update host header
|
||||||
|
httpFields.put(HttpHeader.HOST.toString(),forwardedHost);
|
||||||
|
request.setServerName(null);
|
||||||
|
request.setServerPort(-1);
|
||||||
|
request.getServerName();
|
||||||
|
}
|
||||||
|
else if (forwardedServer != null)
|
||||||
|
{
|
||||||
|
// Use provided server name
|
||||||
|
request.setServerName(forwardedServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forwardedFor != null)
|
||||||
|
{
|
||||||
|
request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forwardedProto != null)
|
||||||
|
{
|
||||||
|
request.setScheme(forwardedProto);
|
||||||
|
if (forwardedProto.equals(config.getSecureScheme()))
|
||||||
|
request.setSecure(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
protected String getLeftMostFieldValue(HttpFields fields, String header)
|
||||||
|
{
|
||||||
|
if (header == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String headerValue = fields.getStringField(header);
|
||||||
|
|
||||||
|
if (headerValue == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int commaIndex = headerValue.indexOf(',');
|
||||||
|
|
||||||
|
if (commaIndex == -1)
|
||||||
|
{
|
||||||
|
// Single value
|
||||||
|
return headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The left-most value is the farthest downstream client
|
||||||
|
return headerValue.substring(0,commaIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.net.InetSocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
|
|
||||||
|
@ -63,14 +64,14 @@ import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(HttpChannel.class);
|
private static final Logger LOG = Log.getLogger(HttpChannel.class);
|
||||||
private static final ThreadLocal<HttpChannel> __currentChannel = new ThreadLocal<>();
|
private static final ThreadLocal<HttpChannel<?>> __currentChannel = new ThreadLocal<>();
|
||||||
|
|
||||||
public static HttpChannel getCurrentHttpChannel()
|
public static HttpChannel<?> getCurrentHttpChannel()
|
||||||
{
|
{
|
||||||
return __currentChannel.get();
|
return __currentChannel.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void setCurrentHttpChannel(HttpChannel channel)
|
protected static void setCurrentHttpChannel(HttpChannel<?> channel)
|
||||||
{
|
{
|
||||||
__currentChannel.set(channel);
|
__currentChannel.set(channel);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
private final AtomicBoolean _committed = new AtomicBoolean();
|
private final AtomicBoolean _committed = new AtomicBoolean();
|
||||||
private final AtomicInteger _requests = new AtomicInteger();
|
private final AtomicInteger _requests = new AtomicInteger();
|
||||||
private final Connector _connector;
|
private final Connector _connector;
|
||||||
private final HttpConfiguration _configuration;
|
private final HttpChannelConfig _configuration;
|
||||||
private final EndPoint _endPoint;
|
private final EndPoint _endPoint;
|
||||||
private final HttpTransport _transport;
|
private final HttpTransport _transport;
|
||||||
private final HttpURI _uri;
|
private final HttpURI _uri;
|
||||||
|
@ -90,7 +91,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
private boolean _expect100Continue = false;
|
private boolean _expect100Continue = false;
|
||||||
private boolean _expect102Processing = false;
|
private boolean _expect102Processing = false;
|
||||||
|
|
||||||
public HttpChannel(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<T> input)
|
public HttpChannel(Connector connector, HttpChannelConfig configuration, EndPoint endPoint, HttpTransport transport, HttpInput<T> input)
|
||||||
{
|
{
|
||||||
_connector = connector;
|
_connector = connector;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
@ -131,7 +132,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
return _connector.getByteBufferPool();
|
return _connector.getByteBufferPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpConfiguration getHttpConfiguration()
|
public HttpChannelConfig getHttpChannelConfig()
|
||||||
{
|
{
|
||||||
return _configuration;
|
return _configuration;
|
||||||
}
|
}
|
||||||
|
@ -237,8 +238,11 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
|
|
||||||
if (_state.isInitial())
|
if (_state.isInitial())
|
||||||
{
|
{
|
||||||
|
_request.setTimeStamp(System.currentTimeMillis());
|
||||||
_request.setDispatcherType(DispatcherType.REQUEST);
|
_request.setDispatcherType(DispatcherType.REQUEST);
|
||||||
getHttpConfiguration().customize(_request);
|
|
||||||
|
for (HttpChannelConfig.Customizer customizer : _configuration.getCustomizers())
|
||||||
|
customizer.customize(getConnector(),_configuration,_request);
|
||||||
getServer().handle(this);
|
getServer().handle(this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -321,7 +325,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
if (_state.isSuspended())
|
if (_state.isSuspended())
|
||||||
{
|
{
|
||||||
HttpFields fields = new HttpFields();
|
HttpFields fields = new HttpFields();
|
||||||
ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, Response.SC_INTERNAL_SERVER_ERROR, null, _request.isHead());
|
ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, HttpStatus.INTERNAL_SERVER_ERROR_500, null, _request.isHead());
|
||||||
boolean committed = commitResponse(info, null, true);
|
boolean committed = commitResponse(info, null, true);
|
||||||
if (!committed)
|
if (!committed)
|
||||||
LOG.warn("Could not send response error 500: "+x);
|
LOG.warn("Could not send response error 500: "+x);
|
||||||
|
@ -515,7 +519,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} content {}", this, item);
|
LOG.debug("{} content {}", this, item);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
HttpInput<T> input = _request.getHttpInput();
|
HttpInput<T> input = (HttpInput<T>)_request.getHttpInput();
|
||||||
input.content(item);
|
input.content(item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
|
||||||
|
public class HttpChannelConfig
|
||||||
|
{
|
||||||
|
private List<Customizer> _customizers=new CopyOnWriteArrayList<>();
|
||||||
|
private int _outputBufferSize=32*1024;
|
||||||
|
private int _requestHeaderSize=8*1024;
|
||||||
|
private int _responseHeaderSize=8*1024;
|
||||||
|
private int _securePort;
|
||||||
|
private String _secureScheme = HttpScheme.HTTPS.asString();
|
||||||
|
|
||||||
|
public interface Customizer
|
||||||
|
{
|
||||||
|
public void customize(Connector connector, HttpChannelConfig channelConfig, Request request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ConnectionFactory
|
||||||
|
{
|
||||||
|
HttpChannelConfig getHttpChannelConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCustomizer(Customizer customizer)
|
||||||
|
{
|
||||||
|
_customizers.add(customizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Customizer> getCustomizers()
|
||||||
|
{
|
||||||
|
return _customizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getCustomizer(Class<T> type)
|
||||||
|
{
|
||||||
|
for (Customizer c : _customizers)
|
||||||
|
if (type.isAssignableFrom(c.getClass()))
|
||||||
|
return (T)c;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOutputBufferSize()
|
||||||
|
{
|
||||||
|
return _outputBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRequestHeaderSize()
|
||||||
|
{
|
||||||
|
return _requestHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getResponseHeaderSize()
|
||||||
|
{
|
||||||
|
return _responseHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSecurePort()
|
||||||
|
{
|
||||||
|
return _securePort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecureScheme()
|
||||||
|
{
|
||||||
|
return _secureScheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomizers(List<Customizer> customizers)
|
||||||
|
{
|
||||||
|
_customizers.clear();
|
||||||
|
_customizers.addAll(customizers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOutputBufferSize(int responseBufferSize)
|
||||||
|
{
|
||||||
|
_outputBufferSize = responseBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestHeaderSize(int requestHeaderSize)
|
||||||
|
{
|
||||||
|
_requestHeaderSize = requestHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponseHeaderSize(int responseHeaderSize)
|
||||||
|
{
|
||||||
|
_responseHeaderSize = responseHeaderSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecurePort(int confidentialPort)
|
||||||
|
{
|
||||||
|
_securePort = confidentialPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecureScheme(String confidentialScheme)
|
||||||
|
{
|
||||||
|
_secureScheme = confidentialScheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x{%d,%d/%d,%s://:%d,%s}",this.getClass().getSimpleName(),hashCode(),_outputBufferSize,_requestHeaderSize,_responseHeaderSize,_secureScheme,_securePort,_customizers);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.server;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.AsyncEvent;
|
import javax.servlet.AsyncEvent;
|
||||||
import javax.servlet.AsyncListener;
|
import javax.servlet.AsyncListener;
|
||||||
|
|
|
@ -1,556 +0,0 @@
|
||||||
//
|
|
||||||
// ========================================================================
|
|
||||||
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// All rights reserved. This program and the accompanying materials
|
|
||||||
// are made available under the terms of the Eclipse Public License v1.0
|
|
||||||
// and Apache License v2.0 which accompanies this distribution.
|
|
||||||
//
|
|
||||||
// The Eclipse Public License is available at
|
|
||||||
// http://www.eclipse.org/legal/epl-v10.html
|
|
||||||
//
|
|
||||||
// The Apache License v2.0 is available at
|
|
||||||
// http://www.opensource.org/licenses/apache2.0.php
|
|
||||||
//
|
|
||||||
// You may elect to redistribute this code under either of these licenses.
|
|
||||||
// ========================================================================
|
|
||||||
//
|
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
import javax.net.ssl.SSLSession;
|
|
||||||
import javax.servlet.ServletRequest;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
|
||||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
|
||||||
import org.eclipse.jetty.server.ssl.SslCertificates;
|
|
||||||
import org.eclipse.jetty.util.component.AggregateLifeCycle;
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|
||||||
|
|
||||||
|
|
||||||
public class HttpConfiguration extends AggregateLifeCycle
|
|
||||||
{
|
|
||||||
static final Logger LOG = Log.getLogger(HttpConfiguration.class);
|
|
||||||
|
|
||||||
private final SslContextFactory _sslContextFactory;
|
|
||||||
private final boolean _ssl;
|
|
||||||
|
|
||||||
private String _integralScheme = HttpScheme.HTTPS.asString();
|
|
||||||
private int _integralPort = 0;
|
|
||||||
private String _confidentialScheme = HttpScheme.HTTPS.asString();
|
|
||||||
private int _confidentialPort = 0;
|
|
||||||
private boolean _forwarded;
|
|
||||||
private String _hostHeader;
|
|
||||||
private String _forwardedHostHeader = HttpHeader.X_FORWARDED_HOST.toString();
|
|
||||||
private String _forwardedServerHeader = HttpHeader.X_FORWARDED_SERVER.toString();
|
|
||||||
private String _forwardedForHeader = HttpHeader.X_FORWARDED_FOR.toString();
|
|
||||||
private String _forwardedProtoHeader = HttpHeader.X_FORWARDED_PROTO.toString();
|
|
||||||
private String _forwardedCipherSuiteHeader;
|
|
||||||
private String _forwardedSslSessionIdHeader;
|
|
||||||
private int _requestHeaderSize=8*1024;
|
|
||||||
private int _requestBufferSize=16*1024;
|
|
||||||
private int _responseHeaderSize=8*1024;
|
|
||||||
private int _responseBufferSize=32*1024;
|
|
||||||
|
|
||||||
public HttpConfiguration(SslContextFactory sslContextFactory,boolean ssl)
|
|
||||||
{
|
|
||||||
_sslContextFactory=sslContextFactory!=null?sslContextFactory:ssl?new SslContextFactory(SslContextFactory.DEFAULT_KEYSTORE_PATH):null;
|
|
||||||
_ssl=ssl;
|
|
||||||
if (_sslContextFactory!=null)
|
|
||||||
addBean(_sslContextFactory,sslContextFactory==null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SslContextFactory getSslContextFactory()
|
|
||||||
{
|
|
||||||
return _sslContextFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSecure()
|
|
||||||
{
|
|
||||||
return _ssl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRequestHeaderSize()
|
|
||||||
{
|
|
||||||
return _requestHeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestHeaderSize(int requestHeaderSize)
|
|
||||||
{
|
|
||||||
_requestHeaderSize = requestHeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRequestBufferSize()
|
|
||||||
{
|
|
||||||
return _requestBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestBufferSize(int requestBufferSize)
|
|
||||||
{
|
|
||||||
_requestBufferSize = requestBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getResponseHeaderSize()
|
|
||||||
{
|
|
||||||
return _responseHeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResponseHeaderSize(int responseHeaderSize)
|
|
||||||
{
|
|
||||||
_responseHeaderSize = responseHeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getResponseBufferSize()
|
|
||||||
{
|
|
||||||
return _responseBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResponseBufferSize(int responseBufferSize)
|
|
||||||
{
|
|
||||||
_responseBufferSize = responseBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Allow the Listener a chance to customise the request. before the server
|
|
||||||
* does its stuff. <br>
|
|
||||||
* This allows the required attributes to be set for SSL requests. <br>
|
|
||||||
* The requirements of the Servlet specs are:
|
|
||||||
* <ul>
|
|
||||||
* <li> an attribute named "javax.servlet.request.ssl_session_id" of type
|
|
||||||
* String (since Servlet Spec 3.0).</li>
|
|
||||||
* <li> an attribute named "javax.servlet.request.cipher_suite" of type
|
|
||||||
* String.</li>
|
|
||||||
* <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
|
|
||||||
* <li> an attribute named "javax.servlet.request.X509Certificate" of type
|
|
||||||
* java.security.cert.X509Certificate[]. This is an array of objects of type
|
|
||||||
* X509Certificate, the order of this array is defined as being in ascending
|
|
||||||
* order of trust. The first certificate in the chain is the one set by the
|
|
||||||
* client, the next is the one used to authenticate the first, and so on.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public void customize(Request request) throws IOException
|
|
||||||
{
|
|
||||||
if (isSecure())
|
|
||||||
{
|
|
||||||
request.setScheme(HttpScheme.HTTPS.asString());
|
|
||||||
SslConnection.DecryptedEndPoint ssl_endp = (SslConnection.DecryptedEndPoint)request.getHttpChannel().getEndPoint();
|
|
||||||
SslConnection sslConnection = ssl_endp.getSslConnection();
|
|
||||||
SSLEngine sslEngine=sslConnection.getSSLEngine();
|
|
||||||
SslCertificates.customize(sslEngine,request);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.setTimeStamp(System.currentTimeMillis());
|
|
||||||
if (isForwarded())
|
|
||||||
checkForwardedHeaders(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
protected void checkForwardedHeaders(Request request) throws IOException
|
|
||||||
{
|
|
||||||
HttpFields httpFields = request.getHttpFields();
|
|
||||||
|
|
||||||
// Do SSL first
|
|
||||||
if (getForwardedCipherSuiteHeader()!=null)
|
|
||||||
{
|
|
||||||
String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
|
|
||||||
if (cipher_suite!=null)
|
|
||||||
request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
|
|
||||||
}
|
|
||||||
if (getForwardedSslSessionIdHeader()!=null)
|
|
||||||
{
|
|
||||||
String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
|
|
||||||
if(ssl_session_id!=null)
|
|
||||||
{
|
|
||||||
request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
|
|
||||||
request.setScheme(HttpScheme.HTTPS.asString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieving headers from the request
|
|
||||||
String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
|
|
||||||
String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
|
|
||||||
String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
|
|
||||||
String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
|
|
||||||
|
|
||||||
if (_hostHeader != null)
|
|
||||||
{
|
|
||||||
// Update host header
|
|
||||||
httpFields.put(HttpHeader.HOST.toString(),_hostHeader);
|
|
||||||
request.setServerName(null);
|
|
||||||
request.setServerPort(-1);
|
|
||||||
request.getServerName();
|
|
||||||
}
|
|
||||||
else if (forwardedHost != null)
|
|
||||||
{
|
|
||||||
// Update host header
|
|
||||||
httpFields.put(HttpHeader.HOST.toString(),forwardedHost);
|
|
||||||
request.setServerName(null);
|
|
||||||
request.setServerPort(-1);
|
|
||||||
request.getServerName();
|
|
||||||
}
|
|
||||||
else if (forwardedServer != null)
|
|
||||||
{
|
|
||||||
// Use provided server name
|
|
||||||
request.setServerName(forwardedServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forwardedFor != null)
|
|
||||||
{
|
|
||||||
request.setRemoteAddr(new InetSocketAddress(forwardedFor,request.getRemotePort()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (forwardedProto != null)
|
|
||||||
{
|
|
||||||
request.setScheme(forwardedProto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
protected String getLeftMostFieldValue(HttpFields fields, String header)
|
|
||||||
{
|
|
||||||
if (header == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
String headerValue = fields.getStringField(header);
|
|
||||||
|
|
||||||
if (headerValue == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
int commaIndex = headerValue.indexOf(',');
|
|
||||||
|
|
||||||
if (commaIndex == -1)
|
|
||||||
{
|
|
||||||
// Single value
|
|
||||||
return headerValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The left-most value is the farthest downstream client
|
|
||||||
return headerValue.substring(0,commaIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
|
||||||
* @see org.eclipse.jetty.server.Connector#getConfidentialPort()
|
|
||||||
*/
|
|
||||||
public int getConfidentialPort()
|
|
||||||
{
|
|
||||||
return _confidentialPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
|
||||||
* @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
|
|
||||||
*/
|
|
||||||
public String getConfidentialScheme()
|
|
||||||
{
|
|
||||||
return _confidentialScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* The request is integral IFF it is secure AND the server port
|
|
||||||
* matches any configured {@link #getIntegralPort()}.
|
|
||||||
* This allows separation of listeners providing INTEGRAL versus
|
|
||||||
* CONFIDENTIAL constraints, such as one SSL listener configured to require
|
|
||||||
* client certs providing CONFIDENTIAL, whereas another SSL listener not
|
|
||||||
* requiring client certs providing mere INTEGRAL constraints.
|
|
||||||
* <p>
|
|
||||||
* The request is secure if it is SSL or it {@link #isForwarded()} is true
|
|
||||||
* and the scheme matches {@link #getIntegralScheme()()}
|
|
||||||
*/
|
|
||||||
public boolean isIntegral(Request request)
|
|
||||||
{
|
|
||||||
boolean https = isSecure() || _forwarded && _integralScheme.equalsIgnoreCase(request.getScheme());
|
|
||||||
int iPort=getIntegralPort();
|
|
||||||
boolean port = iPort<=0||iPort==request.getServerPort();
|
|
||||||
return https && port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
|
||||||
* @see org.eclipse.jetty.server.Connector#getConfidentialPort()
|
|
||||||
*/
|
|
||||||
public int getIntegralPort()
|
|
||||||
{
|
|
||||||
return _integralPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
|
||||||
* @see org.eclipse.jetty.server.Connector#getIntegralScheme()
|
|
||||||
*/
|
|
||||||
public String getIntegralScheme()
|
|
||||||
{
|
|
||||||
return _integralScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* The request is confidential IFF it is secure AND the server port
|
|
||||||
* matches any configured {@link #getConfidentialPort()}.
|
|
||||||
* This allows separation of listeners providing INTEGRAL versus
|
|
||||||
* CONFIDENTIAL constraints, such as one SSL listener configured to require
|
|
||||||
* client certs providing CONFIDENTIAL, whereas another SSL listener not
|
|
||||||
* requiring client certs providing mere INTEGRAL constraints.
|
|
||||||
* <p>
|
|
||||||
* The request is secure if it is SSL or it {@link #isForwarded()} is true
|
|
||||||
* and the scheme matches {@link #getConfidentialScheme()}
|
|
||||||
*/
|
|
||||||
public boolean isConfidential(Request request)
|
|
||||||
{
|
|
||||||
boolean https = isSecure() || _forwarded && _confidentialScheme.equalsIgnoreCase(request.getScheme());
|
|
||||||
int confidentialPort=getConfidentialPort();
|
|
||||||
boolean port = confidentialPort<=0||confidentialPort==request.getServerPort();
|
|
||||||
return https && port;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param confidentialPort
|
|
||||||
* The confidentialPort to set.
|
|
||||||
*/
|
|
||||||
public void setConfidentialPort(int confidentialPort)
|
|
||||||
{
|
|
||||||
_confidentialPort = confidentialPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param confidentialScheme
|
|
||||||
* The confidentialScheme to set.
|
|
||||||
*/
|
|
||||||
public void setConfidentialScheme(String confidentialScheme)
|
|
||||||
{
|
|
||||||
_confidentialScheme = confidentialScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param integralPort
|
|
||||||
* The integralPort to set.
|
|
||||||
*/
|
|
||||||
public void setIntegralPort(int integralPort)
|
|
||||||
{
|
|
||||||
_integralPort = integralPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param integralScheme
|
|
||||||
* The integralScheme to set.
|
|
||||||
*/
|
|
||||||
public void setIntegralScheme(String integralScheme)
|
|
||||||
{
|
|
||||||
_integralScheme = integralScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Is reverse proxy handling on?
|
|
||||||
*
|
|
||||||
* @return true if this connector is checking the x-forwarded-for/host/server headers
|
|
||||||
*/
|
|
||||||
public boolean isForwarded()
|
|
||||||
{
|
|
||||||
return _forwarded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol,
|
|
||||||
* host, server and client ip.
|
|
||||||
*
|
|
||||||
* @param check true if this connector is checking the x-forwarded-for/host/server headers
|
|
||||||
* @see #setForwardedForHeader(String)
|
|
||||||
* @see #setForwardedHostHeader(String)
|
|
||||||
* @see #setForwardedProtoHeader(String)
|
|
||||||
* @see #setForwardedServerHeader(String)
|
|
||||||
*/
|
|
||||||
public void setForwarded(boolean check)
|
|
||||||
{
|
|
||||||
if (check)
|
|
||||||
LOG.debug("{} is forwarded",this);
|
|
||||||
_forwarded = check;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public String getHostHeader()
|
|
||||||
{
|
|
||||||
return _hostHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
|
|
||||||
* This value is only used if {@link #isForwarded()} is true.
|
|
||||||
*
|
|
||||||
* @param hostHeader
|
|
||||||
* The value of the host header to force.
|
|
||||||
*/
|
|
||||||
public void setHostHeader(String hostHeader)
|
|
||||||
{
|
|
||||||
_hostHeader = hostHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public String getForwardedHostHeader()
|
|
||||||
{
|
|
||||||
return _forwardedHostHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param forwardedHostHeader
|
|
||||||
* The header name for forwarded hosts (default x-forwarded-host)
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public void setForwardedHostHeader(String forwardedHostHeader)
|
|
||||||
{
|
|
||||||
_forwardedHostHeader = forwardedHostHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return the header name for forwarded server.
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public String getForwardedServerHeader()
|
|
||||||
{
|
|
||||||
return _forwardedServerHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param forwardedServerHeader
|
|
||||||
* The header name for forwarded server (default x-forwarded-server)
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public void setForwardedServerHeader(String forwardedServerHeader)
|
|
||||||
{
|
|
||||||
_forwardedServerHeader = forwardedServerHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return the forwarded for header
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public String getForwardedForHeader()
|
|
||||||
{
|
|
||||||
return _forwardedForHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param forwardedRemoteAddressHeader
|
|
||||||
* The header name for forwarded for (default x-forwarded-for)
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public void setForwardedForHeader(String forwardedRemoteAddressHeader)
|
|
||||||
{
|
|
||||||
_forwardedForHeader = forwardedRemoteAddressHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Get the forwardedProtoHeader.
|
|
||||||
*
|
|
||||||
* @return the forwardedProtoHeader (default X-Forwarded-For)
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public String getForwardedProtoHeader()
|
|
||||||
{
|
|
||||||
return _forwardedProtoHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* Set the forwardedProtoHeader.
|
|
||||||
*
|
|
||||||
* @param forwardedProtoHeader
|
|
||||||
* the forwardedProtoHeader to set (default X-Forwarded-For)
|
|
||||||
* @see #setForwarded(boolean)
|
|
||||||
*/
|
|
||||||
public void setForwardedProtoHeader(String forwardedProtoHeader)
|
|
||||||
{
|
|
||||||
_forwardedProtoHeader = forwardedProtoHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The header name holding a forwarded cipher suite (default null)
|
|
||||||
*/
|
|
||||||
public String getForwardedCipherSuiteHeader()
|
|
||||||
{
|
|
||||||
return _forwardedCipherSuiteHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param forwardedCipherSuite
|
|
||||||
* The header name holding a forwarded cipher suite (default null)
|
|
||||||
*/
|
|
||||||
public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
|
|
||||||
{
|
|
||||||
_forwardedCipherSuiteHeader = forwardedCipherSuite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @return The header name holding a forwarded SSL Session ID (default null)
|
|
||||||
*/
|
|
||||||
public String getForwardedSslSessionIdHeader()
|
|
||||||
{
|
|
||||||
return _forwardedSslSessionIdHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
/**
|
|
||||||
* @param forwardedSslSessionId
|
|
||||||
* The header name holding a forwarded SSL Session ID (default null)
|
|
||||||
*/
|
|
||||||
public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
|
|
||||||
{
|
|
||||||
_forwardedSslSessionIdHeader = forwardedSslSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
@Override
|
|
||||||
protected void doStart() throws Exception
|
|
||||||
{
|
|
||||||
if (_sslContextFactory!=null)
|
|
||||||
{
|
|
||||||
_sslContextFactory.checkKeyStore();
|
|
||||||
|
|
||||||
super.doStart();
|
|
||||||
|
|
||||||
SSLEngine sslEngine = _sslContextFactory.newSSLEngine();
|
|
||||||
|
|
||||||
sslEngine.setUseClientMode(false);
|
|
||||||
|
|
||||||
SSLSession sslSession = sslEngine.getSession();
|
|
||||||
|
|
||||||
if (getRequestHeaderSize()<sslSession.getApplicationBufferSize())
|
|
||||||
setRequestHeaderSize(sslSession.getApplicationBufferSize());
|
|
||||||
if (getRequestBufferSize()<sslSession.getApplicationBufferSize())
|
|
||||||
setRequestBufferSize(sslSession.getApplicationBufferSize());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
super.doStart();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,18 +45,18 @@ import org.eclipse.jetty.util.log.Logger;
|
||||||
*/
|
*/
|
||||||
public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport
|
public class HttpConnection extends AbstractConnection implements Runnable, HttpTransport
|
||||||
{
|
{
|
||||||
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclispe.jetty.server.HttpConnection.UPGRADE";
|
public static final String UPGRADE_CONNECTION_ATTRIBUTE = "org.eclipse.jetty.server.HttpConnection.UPGRADE";
|
||||||
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
||||||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
||||||
|
|
||||||
private final HttpConfiguration _configuration;
|
private final HttpChannelConfig _config;
|
||||||
private final Connector _connector;
|
private final Connector _connector;
|
||||||
private final ByteBufferPool _bufferPool; // TODO: remove field, use a _connector.getByteBufferPool()
|
private final ByteBufferPool _bufferPool;
|
||||||
private final HttpGenerator _generator;
|
private final HttpGenerator _generator;
|
||||||
private final HttpChannelOverHttp _channel;
|
private final HttpChannelOverHttp _channel;
|
||||||
private final HttpParser _parser;
|
private final HttpParser _parser;
|
||||||
private ByteBuffer _requestBuffer = null;
|
private volatile ByteBuffer _requestBuffer = null;
|
||||||
private ByteBuffer _chunk = null;
|
private volatile ByteBuffer _chunk = null;
|
||||||
|
|
||||||
public static HttpConnection getCurrentConnection()
|
public static HttpConnection getCurrentConnection()
|
||||||
{
|
{
|
||||||
|
@ -67,12 +67,17 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
{
|
{
|
||||||
__currentConnection.set(connection);
|
__currentConnection.set(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpChannelConfig getHttpChannelConfig()
|
||||||
|
{
|
||||||
|
return _config;
|
||||||
|
}
|
||||||
|
|
||||||
public HttpConnection(HttpConfiguration config, Connector connector, EndPoint endPoint)
|
public HttpConnection(HttpChannelConfig config, Connector connector, EndPoint endPoint)
|
||||||
{
|
{
|
||||||
super(endPoint, connector.getExecutor());
|
super(endPoint, connector.getExecutor());
|
||||||
|
|
||||||
_configuration = config;
|
_config = config;
|
||||||
_connector = connector;
|
_connector = connector;
|
||||||
_bufferPool = _connector.getByteBufferPool();
|
_bufferPool = _connector.getByteBufferPool();
|
||||||
_generator = new HttpGenerator(); // TODO: consider moving the generator to the transport, where it belongs
|
_generator = new HttpGenerator(); // TODO: consider moving the generator to the transport, where it belongs
|
||||||
|
@ -98,7 +103,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
return _connector;
|
return _connector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpChannel getHttpChannel()
|
public HttpChannel<?> getHttpChannel()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _channel;
|
||||||
}
|
}
|
||||||
|
@ -145,8 +150,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
{
|
{
|
||||||
if (_requestBuffer != null && !_requestBuffer.hasRemaining())
|
if (_requestBuffer != null && !_requestBuffer.hasRemaining())
|
||||||
{
|
{
|
||||||
_bufferPool.release(_requestBuffer);
|
ByteBuffer buffer=_requestBuffer;
|
||||||
_requestBuffer = null;
|
_requestBuffer=null;
|
||||||
|
_bufferPool.release(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +181,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
if (!event && BufferUtil.isEmpty(_requestBuffer))
|
if (!event && BufferUtil.isEmpty(_requestBuffer))
|
||||||
{
|
{
|
||||||
if (_requestBuffer == null)
|
if (_requestBuffer == null)
|
||||||
_requestBuffer = _bufferPool.acquire(_configuration.getRequestHeaderSize(), false);
|
_requestBuffer = _bufferPool.acquire(getInputBufferSize(), false);
|
||||||
|
|
||||||
int filled = getEndPoint().fill(_requestBuffer);
|
int filled = getEndPoint().fill(_requestBuffer);
|
||||||
if (filled==0) // Do a retry on fill 0 (optimisation for SSL connections)
|
if (filled==0) // Do a retry on fill 0 (optimisation for SSL connections)
|
||||||
|
@ -315,7 +321,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
{
|
{
|
||||||
case NEED_HEADER:
|
case NEED_HEADER:
|
||||||
{
|
{
|
||||||
header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), false);
|
header = _bufferPool.acquire(_config.getResponseHeaderSize(), false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
case NEED_CHUNK:
|
case NEED_CHUNK:
|
||||||
|
@ -533,7 +539,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
|
|
||||||
// We will need a buffer to read into
|
// We will need a buffer to read into
|
||||||
if (_requestBuffer==null)
|
if (_requestBuffer==null)
|
||||||
_requestBuffer=_bufferPool.acquire(_configuration.getRequestBufferSize(),false);
|
_requestBuffer=_bufferPool.acquire(getInputBufferSize(),false);
|
||||||
|
|
||||||
// read some data
|
// read some data
|
||||||
int filled=getEndPoint().fill(_requestBuffer);
|
int filled=getEndPoint().fill(_requestBuffer);
|
||||||
|
@ -590,9 +596,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
||||||
|
|
||||||
private class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
|
private class HttpChannelOverHttp extends HttpChannel<ByteBuffer>
|
||||||
{
|
{
|
||||||
public HttpChannelOverHttp(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
|
public HttpChannelOverHttp(Connector connector, HttpChannelConfig config, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
|
||||||
{
|
{
|
||||||
super(connector,configuration,endPoint,transport,input);
|
super(connector,config,endPoint,transport,input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -19,39 +19,41 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.nio.channels.SocketChannel;
|
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
|
|
||||||
public class HttpServerConnectionFactory implements ConnectionFactory
|
public class HttpConnectionFactory extends AbstractConnectionFactory implements HttpChannelConfig.ConnectionFactory
|
||||||
{
|
{
|
||||||
private final Connector connector;
|
private final HttpChannelConfig _config;
|
||||||
private final HttpConfiguration configuration;
|
|
||||||
|
|
||||||
public HttpServerConnectionFactory(Connector connector)
|
public HttpConnectionFactory()
|
||||||
{
|
{
|
||||||
this(connector, new HttpConfiguration(connector.getSslContextFactory(), connector.getSslContextFactory() != null));
|
this(new HttpChannelConfig());
|
||||||
|
setInputBufferSize(16384);
|
||||||
}
|
}
|
||||||
public HttpServerConnectionFactory(Connector connector, HttpConfiguration configuration)
|
|
||||||
|
public HttpConnectionFactory(@Name("config") HttpChannelConfig config)
|
||||||
{
|
{
|
||||||
this.connector = connector;
|
super(HttpVersion.HTTP_1_1.toString());
|
||||||
this.configuration = configuration;
|
_config=config;
|
||||||
|
addBean(_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Connector getConnector()
|
@Override
|
||||||
|
public HttpChannelConfig getHttpChannelConfig()
|
||||||
{
|
{
|
||||||
return connector;
|
return _config;
|
||||||
}
|
|
||||||
|
|
||||||
public HttpConfiguration getHttpConfiguration()
|
|
||||||
{
|
|
||||||
return configuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment)
|
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||||
{
|
{
|
||||||
return new HttpConnection(getHttpConfiguration(), getConnector(), endPoint);
|
HttpConnection connection = new HttpConnection(_config, connector, endPoint);
|
||||||
|
connection.setInputBufferSize(getInputBufferSize()); // TODO constructor injection
|
||||||
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InterruptedIOException;
|
import java.io.InterruptedIOException;
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.EofException;
|
import org.eclipse.jetty.io.EofException;
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
|
@ -54,7 +55,7 @@ public class HttpOutput extends ServletOutputStream
|
||||||
public HttpOutput(HttpChannel channel)
|
public HttpOutput(HttpChannel channel)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_bufferSize = _channel.getHttpConfiguration().getResponseBufferSize();
|
_bufferSize = _channel.getHttpChannelConfig().getOutputBufferSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWritten()
|
public boolean isWritten()
|
||||||
|
@ -209,7 +210,7 @@ public class HttpOutput extends ServletOutputStream
|
||||||
HttpContent httpContent = (HttpContent)content;
|
HttpContent httpContent = (HttpContent)content;
|
||||||
Response response = _channel.getResponse();
|
Response response = _channel.getResponse();
|
||||||
String contentType = httpContent.getContentType();
|
String contentType = httpContent.getContentType();
|
||||||
if (contentType != null)
|
if (contentType != null && !response.getHttpFields().containsKey(HttpHeader.CONTENT_TYPE.asString()))
|
||||||
response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType);
|
response.getHttpFields().put(HttpHeader.CONTENT_TYPE, contentType);
|
||||||
|
|
||||||
if (httpContent.getContentLength() > 0)
|
if (httpContent.getContentLength() > 0)
|
||||||
|
|
|
@ -27,19 +27,44 @@ import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
public class HttpServerConnector extends SelectChannelConnector
|
public class HttpServerConnector extends SelectChannelConnector
|
||||||
{
|
{
|
||||||
public HttpServerConnector(Server server)
|
public HttpServerConnector(
|
||||||
|
@Name("server") Server server)
|
||||||
{
|
{
|
||||||
this(server, null);
|
this(server,null,null,null,null,null,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpServerConnector(Server server, SslContextFactory sslContextFactory)
|
public HttpServerConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("sslContextFactory") SslContextFactory sslContextFactory)
|
||||||
{
|
{
|
||||||
this(server, null, null, null, sslContextFactory, 0, 0);
|
this(server,null,sslContextFactory, null, null, null, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpServerConnector(@Name("server") Server server, @Name("executor") Executor executor, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool pool, @Name("sslContextFactory") SslContextFactory sslContextFactory, @Name("acceptors") int acceptors, @Name("selectors") int selectors)
|
public HttpServerConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("connectionFactory") HttpConnectionFactory connectionFactory)
|
||||||
{
|
{
|
||||||
super(server, executor, scheduler, pool, sslContextFactory, acceptors, selectors);
|
this(server,connectionFactory,null, null, null, null, 0, 0);
|
||||||
setDefaultConnectionFactory(new HttpServerConnectionFactory(this));
|
}
|
||||||
|
|
||||||
|
public HttpServerConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("connectionFactory") HttpConnectionFactory connectionFactory,
|
||||||
|
@Name("sslContextFactory") SslContextFactory sslContextFactory)
|
||||||
|
{
|
||||||
|
this(server,connectionFactory,sslContextFactory, null, null, null, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpServerConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("connectionFactory") HttpConnectionFactory connectionFactory,
|
||||||
|
@Name("sslContextFactory") SslContextFactory sslContextFactory,
|
||||||
|
@Name("executor") Executor executor,
|
||||||
|
@Name("scheduler") Scheduler scheduler,
|
||||||
|
@Name("bufferPool") ByteBufferPool pool,
|
||||||
|
@Name("acceptors") int acceptors,
|
||||||
|
@Name("selectors") int selectors)
|
||||||
|
{
|
||||||
|
super(server,executor,scheduler,pool,acceptors,selectors,AbstractConnectionFactory.getFactories(sslContextFactory,connectionFactory==null?new HttpConnectionFactory():connectionFactory));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,10 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteArrayEndPoint;
|
import org.eclipse.jetty.io.ByteArrayEndPoint;
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
|
||||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
@ -41,22 +38,31 @@ public class LocalConnector extends AbstractConnector
|
||||||
{
|
{
|
||||||
private final BlockingQueue<LocalEndPoint> _connects = new LinkedBlockingQueue<>();
|
private final BlockingQueue<LocalEndPoint> _connects = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
|
|
||||||
|
public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, ConnectionFactory... factories)
|
||||||
|
{
|
||||||
|
super(server,executor,scheduler,pool,acceptors,factories);
|
||||||
|
setIdleTimeout(30000);
|
||||||
|
}
|
||||||
|
|
||||||
public LocalConnector(Server server)
|
public LocalConnector(Server server)
|
||||||
{
|
{
|
||||||
this(server,null);
|
this(server, null, null, null, 0, new HttpConnectionFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalConnector(Server server, SslContextFactory sslContextFactory)
|
public LocalConnector(Server server, SslContextFactory sslContextFactory)
|
||||||
{
|
{
|
||||||
this(server, null, null, null, sslContextFactory, 0);
|
this(server, null, null, null, 0,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public LocalConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool,
|
public LocalConnector(Server server, ConnectionFactory connectionFactory)
|
||||||
SslContextFactory sslContextFactory, int acceptors)
|
|
||||||
{
|
{
|
||||||
super(server,executor,scheduler,pool, sslContextFactory, acceptors);
|
this(server, null, null, null, 0, connectionFactory);
|
||||||
setIdleTimeout(30000);
|
}
|
||||||
setDefaultConnectionFactory(new HttpServerConnectionFactory(this));
|
|
||||||
|
public LocalConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory)
|
||||||
|
{
|
||||||
|
this(server, null, null, null, 0,AbstractConnectionFactory.getFactories(sslContextFactory,connectionFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -159,29 +165,10 @@ public class LocalConnector extends AbstractConnector
|
||||||
LocalEndPoint endPoint = _connects.take();
|
LocalEndPoint endPoint = _connects.take();
|
||||||
endPoint.onOpen();
|
endPoint.onOpen();
|
||||||
|
|
||||||
SslContextFactory sslContextFactory = getSslContextFactory();
|
Connection connection = getDefaultConnectionFactory().newConnection(this, endPoint);
|
||||||
if (sslContextFactory != null)
|
endPoint.setConnection(connection);
|
||||||
{
|
connectionOpened(connection);
|
||||||
SSLEngine engine = sslContextFactory.newSSLEngine(endPoint.getRemoteAddress());
|
connection.onOpen();
|
||||||
engine.setUseClientMode(false);
|
|
||||||
|
|
||||||
SslConnection sslConnection = new SslConnection(getByteBufferPool(), getExecutor(), endPoint, engine);
|
|
||||||
endPoint.setConnection(sslConnection);
|
|
||||||
connectionOpened(sslConnection);
|
|
||||||
sslConnection.onOpen();
|
|
||||||
|
|
||||||
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
|
|
||||||
Connection connection = getDefaultConnectionFactory().newConnection(null, appEndPoint, null);
|
|
||||||
appEndPoint.setConnection(connection);
|
|
||||||
connection.onOpen();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Connection connection = getDefaultConnectionFactory().newConnection(null, endPoint, null);
|
|
||||||
endPoint.setConnection(connection);
|
|
||||||
connectionOpened(connection);
|
|
||||||
connection.onOpen();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LocalEndPoint extends ByteArrayEndPoint
|
public class LocalEndPoint extends ByteArrayEndPoint
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
|
|
@ -37,6 +37,7 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.AsyncListener;
|
import javax.servlet.AsyncListener;
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
|
@ -118,28 +119,30 @@ public class Request implements HttpServletRequest
|
||||||
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
|
private static final Collection<Locale> __defaultLocale = Collections.singleton(Locale.getDefault());
|
||||||
private static final int __NONE = 0, _STREAM = 1, __READER = 2;
|
private static final int __NONE = 0, _STREAM = 1, __READER = 2;
|
||||||
|
|
||||||
private final HttpChannel _channel;
|
private final HttpChannel<?> _channel;
|
||||||
private final HttpFields _fields=new HttpFields();
|
private final HttpFields _fields=new HttpFields();
|
||||||
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
|
private final List<ServletRequestAttributeListener> _requestAttributeListeners=new ArrayList<>();
|
||||||
private final HttpInput _input;
|
private final HttpInput<?> _input;
|
||||||
|
|
||||||
|
private boolean _secure;
|
||||||
private boolean _asyncSupported = true;
|
private boolean _asyncSupported = true;
|
||||||
|
private boolean _newContext;
|
||||||
|
private boolean _cookiesExtracted = false;
|
||||||
|
private boolean _handled = false;
|
||||||
|
private boolean _paramsExtracted;
|
||||||
|
private boolean _requestedSessionIdFromCookie = false;
|
||||||
private volatile Attributes _attributes;
|
private volatile Attributes _attributes;
|
||||||
private Authentication _authentication;
|
private Authentication _authentication;
|
||||||
private MultiMap<String> _baseParameters;
|
private MultiMap<String> _baseParameters;
|
||||||
private String _characterEncoding;
|
private String _characterEncoding;
|
||||||
private ContextHandler.Context _context;
|
private ContextHandler.Context _context;
|
||||||
private boolean _newContext;
|
|
||||||
private String _contextPath;
|
private String _contextPath;
|
||||||
private CookieCutter _cookies;
|
private CookieCutter _cookies;
|
||||||
private boolean _cookiesExtracted = false;
|
|
||||||
private DispatcherType _dispatcherType;
|
private DispatcherType _dispatcherType;
|
||||||
private boolean _handled = false;
|
|
||||||
private int _inputState = __NONE;
|
private int _inputState = __NONE;
|
||||||
private HttpMethod _httpMethod;
|
private HttpMethod _httpMethod;
|
||||||
private String _httpMethodString;
|
private String _httpMethodString;
|
||||||
private MultiMap<String> _parameters;
|
private MultiMap<String> _parameters;
|
||||||
private boolean _paramsExtracted;
|
|
||||||
private String _pathInfo;
|
private String _pathInfo;
|
||||||
private int _port;
|
private int _port;
|
||||||
private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
|
private HttpVersion _httpVersion = HttpVersion.HTTP_1_1;
|
||||||
|
@ -149,7 +152,6 @@ public class Request implements HttpServletRequest
|
||||||
private String _readerEncoding;
|
private String _readerEncoding;
|
||||||
private InetSocketAddress _remote;
|
private InetSocketAddress _remote;
|
||||||
private String _requestedSessionId;
|
private String _requestedSessionId;
|
||||||
private boolean _requestedSessionIdFromCookie = false;
|
|
||||||
private String _requestURI;
|
private String _requestURI;
|
||||||
private Map<Object, HttpSession> _savedNewSessions;
|
private Map<Object, HttpSession> _savedNewSessions;
|
||||||
private String _scheme = URIUtil.HTTP;
|
private String _scheme = URIUtil.HTTP;
|
||||||
|
@ -164,7 +166,7 @@ public class Request implements HttpServletRequest
|
||||||
private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
|
private MultiPartInputStream _multiPartInputStream; //if the request is a multi-part mime
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public Request(HttpChannel channel, HttpInput input)
|
public Request(HttpChannel<?> channel, HttpInput<?> input)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_input = input;
|
_input = input;
|
||||||
|
@ -177,7 +179,7 @@ public class Request implements HttpServletRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public HttpInput getHttpInput()
|
public HttpInput<?> getHttpInput()
|
||||||
{
|
{
|
||||||
return _input;
|
return _input;
|
||||||
}
|
}
|
||||||
|
@ -394,7 +396,7 @@ public class Request implements HttpServletRequest
|
||||||
/**
|
/**
|
||||||
* @return Returns the connection.
|
* @return Returns the connection.
|
||||||
*/
|
*/
|
||||||
public HttpChannel getHttpChannel()
|
public HttpChannel<?> getHttpChannel()
|
||||||
{
|
{
|
||||||
return _channel;
|
return _channel;
|
||||||
}
|
}
|
||||||
|
@ -613,7 +615,6 @@ public class Request implements HttpServletRequest
|
||||||
return Collections.enumeration(__defaultLocale);
|
return Collections.enumeration(__defaultLocale);
|
||||||
|
|
||||||
List<Locale> langs = new ArrayList<>();
|
List<Locale> langs = new ArrayList<>();
|
||||||
int size = acceptLanguage.size();
|
|
||||||
|
|
||||||
// convert to locals
|
// convert to locals
|
||||||
for (String language : acceptLanguage)
|
for (String language : acceptLanguage)
|
||||||
|
@ -1374,7 +1375,13 @@ public class Request implements HttpServletRequest
|
||||||
@Override
|
@Override
|
||||||
public boolean isSecure()
|
public boolean isSecure()
|
||||||
{
|
{
|
||||||
return _channel.getHttpConfiguration().isConfidential(this);
|
return _secure;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
public void setSecure(boolean secure)
|
||||||
|
{
|
||||||
|
_secure=secure;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.servlet.RequestDispatcher;
|
import javax.servlet.RequestDispatcher;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// All rights reserved. This program and the accompanying materials
|
||||||
|
// are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
// and Apache License v2.0 which accompanies this distribution.
|
||||||
|
//
|
||||||
|
// The Eclipse Public License is available at
|
||||||
|
// http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
//
|
||||||
|
// The Apache License v2.0 is available at
|
||||||
|
// http://www.opensource.org/licenses/apache2.0.php
|
||||||
|
//
|
||||||
|
// You may elect to redistribute this code under either of these licenses.
|
||||||
|
// ========================================================================
|
||||||
|
//
|
||||||
|
|
||||||
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLSession;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
|
||||||
|
import org.eclipse.jetty.server.ssl.ServletSSL;
|
||||||
|
import org.eclipse.jetty.server.ssl.SslCertificates;
|
||||||
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
public class SecureRequestCustomizer implements HttpChannelConfig.Customizer
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(SecureRequestCustomizer.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the SSLSession attribute that will contain any cached information.
|
||||||
|
*/
|
||||||
|
public static final String CACHED_INFO_ATTR = CachedInfo.class.getName();
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(Connector connector, HttpChannelConfig channelConfig, Request request)
|
||||||
|
{
|
||||||
|
if (request.getHttpChannel().getEndPoint() instanceof DecryptedEndPoint)
|
||||||
|
{
|
||||||
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
|
request.setSecure(true);
|
||||||
|
SslConnection.DecryptedEndPoint ssl_endp = (DecryptedEndPoint)request.getHttpChannel().getEndPoint();
|
||||||
|
SslConnection sslConnection = ssl_endp.getSslConnection();
|
||||||
|
SSLEngine sslEngine=sslConnection.getSSLEngine();
|
||||||
|
customize(sslEngine,request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/*
|
||||||
|
* Allow the Listener a chance to customise the request. before the server
|
||||||
|
* does its stuff. <br>
|
||||||
|
* This allows the required attributes to be set for SSL requests. <br>
|
||||||
|
* The requirements of the Servlet specs are:
|
||||||
|
* <ul>
|
||||||
|
* <li> an attribute named "javax.servlet.request.ssl_session_id" of type
|
||||||
|
* String (since Servlet Spec 3.0).</li>
|
||||||
|
* <li> an attribute named "javax.servlet.request.cipher_suite" of type
|
||||||
|
* String.</li>
|
||||||
|
* <li> an attribute named "javax.servlet.request.key_size" of type Integer.</li>
|
||||||
|
* <li> an attribute named "javax.servlet.request.X509Certificate" of type
|
||||||
|
* java.security.cert.X509Certificate[]. This is an array of objects of type
|
||||||
|
* X509Certificate, the order of this array is defined as being in ascending
|
||||||
|
* order of trust. The first certificate in the chain is the one set by the
|
||||||
|
* client, the next is the one used to authenticate the first, and so on.
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* HttpRequest to be customised.
|
||||||
|
*/
|
||||||
|
public void customize(SSLEngine sslEngine, Request request)
|
||||||
|
{
|
||||||
|
request.setScheme(HttpScheme.HTTPS.asString());
|
||||||
|
SSLSession sslSession = sslEngine.getSession();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String cipherSuite=sslSession.getCipherSuite();
|
||||||
|
Integer keySize;
|
||||||
|
X509Certificate[] certs;
|
||||||
|
String idStr;
|
||||||
|
|
||||||
|
CachedInfo cachedInfo=(CachedInfo)sslSession.getValue(CACHED_INFO_ATTR);
|
||||||
|
if (cachedInfo!=null)
|
||||||
|
{
|
||||||
|
keySize=cachedInfo.getKeySize();
|
||||||
|
certs=cachedInfo.getCerts();
|
||||||
|
idStr=cachedInfo.getIdStr();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
keySize=new Integer(ServletSSL.deduceKeyLength(cipherSuite));
|
||||||
|
certs=SslCertificates.getCertChain(sslSession);
|
||||||
|
byte[] bytes = sslSession.getId();
|
||||||
|
idStr = TypeUtil.toHexString(bytes);
|
||||||
|
cachedInfo=new CachedInfo(keySize,certs,idStr);
|
||||||
|
sslSession.putValue(CACHED_INFO_ATTR,cachedInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certs!=null)
|
||||||
|
request.setAttribute("javax.servlet.request.X509Certificate",certs);
|
||||||
|
|
||||||
|
request.setAttribute("javax.servlet.request.cipher_suite",cipherSuite);
|
||||||
|
request.setAttribute("javax.servlet.request.key_size",keySize);
|
||||||
|
request.setAttribute("javax.servlet.request.ssl_session_id", idStr);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn(Log.EXCEPTION,e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x",this.getClass().getSimpleName(),hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/**
|
||||||
|
* Simple bundle of information that is cached in the SSLSession. Stores the
|
||||||
|
* effective keySize and the client certificate chain.
|
||||||
|
*/
|
||||||
|
private static class CachedInfo
|
||||||
|
{
|
||||||
|
private final X509Certificate[] _certs;
|
||||||
|
private final Integer _keySize;
|
||||||
|
private final String _idStr;
|
||||||
|
|
||||||
|
CachedInfo(Integer keySize, X509Certificate[] certs,String idStr)
|
||||||
|
{
|
||||||
|
this._keySize=keySize;
|
||||||
|
this._certs=certs;
|
||||||
|
this._idStr=idStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509Certificate[] getCerts()
|
||||||
|
{
|
||||||
|
return _certs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer getKeySize()
|
||||||
|
{
|
||||||
|
return _keySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getIdStr()
|
||||||
|
{
|
||||||
|
return _idStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ import java.nio.channels.ServerSocketChannel;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import javax.net.ssl.SSLEngine;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.io.ByteBufferPool;
|
import org.eclipse.jetty.io.ByteBufferPool;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
@ -37,7 +36,6 @@ import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
import org.eclipse.jetty.io.SelectChannelEndPoint;
|
||||||
import org.eclipse.jetty.io.SelectorManager;
|
import org.eclipse.jetty.io.SelectorManager;
|
||||||
import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
|
import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
|
||||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.annotation.Name;
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
|
@ -57,14 +55,33 @@ public class SelectChannelConnector extends AbstractNetworkConnector
|
||||||
private volatile boolean _reuseAddress = true;
|
private volatile boolean _reuseAddress = true;
|
||||||
private volatile int _lingerTime = -1;
|
private volatile int _lingerTime = -1;
|
||||||
|
|
||||||
public SelectChannelConnector(@Name("server") Server server)
|
|
||||||
|
public SelectChannelConnector(
|
||||||
|
@Name("server") Server server)
|
||||||
{
|
{
|
||||||
this(server, null);
|
this(server,null,null,null,0,0,new HttpConnectionFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectChannelConnector(@Name("server")Server server, @Name("sslContextFactory") SslContextFactory sslContextFactory)
|
public SelectChannelConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("factories") ConnectionFactory... factories)
|
||||||
{
|
{
|
||||||
this(server, null, null, null, sslContextFactory, 0, 0);
|
this(server,null,null,null,0,0,factories);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectChannelConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("sslContextFactory") SslContextFactory sslContextFactory)
|
||||||
|
{
|
||||||
|
this(server,null,null,null,0,0,AbstractConnectionFactory.getFactories(sslContextFactory,new HttpConnectionFactory()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectChannelConnector(
|
||||||
|
@Name("server") Server server,
|
||||||
|
@Name("sslContextFactory") SslContextFactory sslContextFactory,
|
||||||
|
@Name("factories") ConnectionFactory... factories)
|
||||||
|
{
|
||||||
|
this(server,null,null,null,0,0,AbstractConnectionFactory.getFactories(sslContextFactory,factories));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,26 +90,20 @@ public class SelectChannelConnector extends AbstractNetworkConnector
|
||||||
* @param scheduler A scheduler for this connector or null to use the servers scheduler
|
* @param scheduler A scheduler for this connector or null to use the servers scheduler
|
||||||
* @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool}
|
* @param pool A buffer pool for this connector or null to use a default {@link ByteBufferPool}
|
||||||
* @param acceptors the number of acceptor threads to use, or 0 for a default value.
|
* @param acceptors the number of acceptor threads to use, or 0 for a default value.
|
||||||
|
* @param factories Zero or more {@link ConnectionFactory} instances.
|
||||||
*/
|
*/
|
||||||
public SelectChannelConnector(
|
public SelectChannelConnector(
|
||||||
@Name("server") Server server,
|
@Name("server") Server server,
|
||||||
@Name("executor") Executor executor,
|
@Name("executor") Executor executor,
|
||||||
@Name("scheduler") Scheduler scheduler,
|
@Name("scheduler") Scheduler scheduler,
|
||||||
@Name("bufferPool") ByteBufferPool pool,
|
@Name("bufferPool") ByteBufferPool pool,
|
||||||
@Name("sslContextFactory") SslContextFactory sslContextFactory,
|
@Name("acceptors") int acceptors,
|
||||||
@Name("acceptors") int acceptors,
|
@Name("selectors") int selectors,
|
||||||
@Name("selectors") int selectors)
|
@Name("factories") ConnectionFactory... factories)
|
||||||
{
|
{
|
||||||
super(server, executor, scheduler, pool, sslContextFactory, acceptors);
|
super(server,executor,scheduler,pool,acceptors,factories);
|
||||||
_manager = new ConnectorSelectorManager(selectors > 0 ? selectors : Math.max(1, (Runtime.getRuntime().availableProcessors()) / 4));
|
_manager = new ConnectorSelectorManager(selectors > 0 ? selectors : Math.max(1, (Runtime.getRuntime().availableProcessors()) / 4));
|
||||||
addBean(_manager, true);
|
addBean(_manager, true);
|
||||||
|
|
||||||
// TODO: why we need to set the linger time when in SSL mode ?
|
|
||||||
if (sslContextFactory != null)
|
|
||||||
setSoLingerTime(30000);
|
|
||||||
|
|
||||||
// TODO: we hardcode HTTP, but this is a generic connector that should not hardcode anything
|
|
||||||
setDefaultConnectionFactory(new HttpServerConnectionFactory(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -257,25 +268,7 @@ public class SelectChannelConnector extends AbstractNetworkConnector
|
||||||
|
|
||||||
protected Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment)
|
protected Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment)
|
||||||
{
|
{
|
||||||
SslContextFactory sslContextFactory = getSslContextFactory();
|
return getDefaultConnectionFactory().newConnection(this, endPoint);
|
||||||
if (sslContextFactory != null)
|
|
||||||
{
|
|
||||||
SSLEngine engine = sslContextFactory.newSSLEngine(endPoint.getRemoteAddress());
|
|
||||||
engine.setUseClientMode(false);
|
|
||||||
|
|
||||||
SslConnection sslConnection = new SslConnection(getByteBufferPool(), getExecutor(), endPoint, engine);
|
|
||||||
|
|
||||||
EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
|
|
||||||
Connection connection = getDefaultConnectionFactory().newConnection(channel, appEndPoint, attachment);
|
|
||||||
appEndPoint.setConnection(connection);
|
|
||||||
connection.onOpen();
|
|
||||||
|
|
||||||
return sslConnection;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return getDefaultConnectionFactory().newConnection(channel, endPoint, attachment);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -94,7 +95,7 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
public Server(@Name("port")int port)
|
public Server(@Name("port")int port)
|
||||||
{
|
{
|
||||||
this((ThreadPool)null);
|
this((ThreadPool)null);
|
||||||
SelectChannelConnector connector=new SelectChannelConnector(this);
|
SelectChannelConnector connector=new HttpServerConnector(this);
|
||||||
connector.setPort(port);
|
connector.setPort(port);
|
||||||
setConnectors(new Connector[]{connector});
|
setConnectors(new Connector[]{connector});
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ public class Server extends HandlerWrapper implements Attributes
|
||||||
public Server(@Name("address")InetSocketAddress addr)
|
public Server(@Name("address")InetSocketAddress addr)
|
||||||
{
|
{
|
||||||
this((ThreadPool)null);
|
this((ThreadPool)null);
|
||||||
SelectChannelConnector connector=new SelectChannelConnector(this);
|
SelectChannelConnector connector=new HttpServerConnector(this);
|
||||||
connector.setHost(addr.getHostName());
|
connector.setHost(addr.getHostName());
|
||||||
connector.setPort(addr.getPort());
|
connector.setPort(addr.getPort());
|
||||||
setConnectors(new Connector[]{connector});
|
setConnectors(new Connector[]{connector});
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletRequest;
|
import javax.servlet.ServletRequest;
|
||||||
import javax.servlet.ServletRequestWrapper;
|
import javax.servlet.ServletRequestWrapper;
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.ServletResponseWrapper;
|
import javax.servlet.ServletResponseWrapper;
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue