Merge branch 'jetty-9.3.x' of github.com:eclipse/jetty.project into jetty-9.3.x
This commit is contained in:
commit
4e426e9be3
47
VERSION.txt
47
VERSION.txt
|
@ -1,5 +1,48 @@
|
|||
jetty-9.3.8-SNAPSHOT
|
||||
|
||||
jetty-9.3.8.RC0 - 25 February 2016
|
||||
+ 81 Exception not always thrown in Jetty to application when upload part is
|
||||
too big
|
||||
+ 82 Request.getPart() that results in Exception still allows other parts to
|
||||
be fetched
|
||||
+ 251 Removing SSLEngine.beginHandshake() calls
|
||||
+ 285 PathContentProvider - Use of Direct buffers without pooling
|
||||
+ 298 qtp threads spin-locked in MBeanContainer.beanAdded
|
||||
+ 342 Reintroducing Response parameter to logExtended
|
||||
+ 344 init script does not properly display status of a non running service
|
||||
+ 346 HttpParser RFC2616 Compliance mode
|
||||
+ 347 Avoid sending request using a connection that is idle timing out
|
||||
+ 352 Integrate session idling for MongoSessionManager
|
||||
+ 354 Spin loop in case of exception thrown during accept()
|
||||
+ 355 Improve close behaviour
|
||||
+ 478918 Change javax.servlet.error,forward,include literals to
|
||||
RequestDispatcher constants
|
||||
+ 484446 InputStreamResponseListener's InputStream uses default read (3) and
|
||||
blocks early on never-ending response.
|
||||
+ 485306 HttpParser (HttpURI) mistaking basic auth password as a port number
|
||||
+ 485469 permessage-deflate extension causes protocol error in Firefox/Chrome
|
||||
+ 486394 Restore MultiPartFilter behavior with regards to temp file access
|
||||
+ 486497 NPE in MappedLoginService
|
||||
+ 486511 Server.getURI() returns wrong scheme on SSL/HTTPS
|
||||
+ 486530 Handler added to WebAppContext prevents ServletContext initialization
|
||||
+ 486589 HttpRequest has a wrong HTTP Version in HTTP/2.
|
||||
+ 486604 Add debug logging of ErrorPageErrorHandler logic
|
||||
+ 486674 Quickstart path attribute normalization should be based on longest
|
||||
path match
|
||||
+ 486829 Cancel stream error after a failed request with the HTTP/2.0 client.
|
||||
+ 486877 Google Chrome flagging 'obsolete cipher suite' in Jetty and will soon
|
||||
issue broken padlock
|
||||
+ 486930 Selector does not correctly handle rejected execution exception
|
||||
+ 487158 Switched SCM URIs to github
|
||||
+ 487197 Deflater/Inflater memory leak with WebSocket permessage-deflate
|
||||
extension
|
||||
+ 487198 ContextScopeListener should be called on context start and stop
|
||||
+ 487277 Introduce http-forwarded module for X-Forwarded support
|
||||
+ 487354 Aborted request or response does not send RST_STREAM frame.
|
||||
+ 487511 Jetty HTTP won't work on turkish systems.
|
||||
+ 487714 Avoid NPE in close race for async write
|
||||
+ 487750 HTTP/2 push must not be recursive.
|
||||
|
||||
jetty-9.2.15.v20160210 - 10 February 2016
|
||||
+ 482042 New API, Allow customization of ServletHandler path mapping
|
||||
+ 482243 Fixed GzipHandler for Include.
|
||||
|
@ -21,9 +64,7 @@ jetty-9.2.15.v20160210 - 10 February 2016
|
|||
+ 485535 jetty.sh results in FAILED when running service restart
|
||||
+ 485663 NullPointerException in WebSocketSession during upgrade with DEBUG
|
||||
logging
|
||||
+ 485712 Quickstart web.xml is absolute
|
||||
|
||||
jetty-9.3.7.v20160115 - 15 January 2016
|
||||
+ jetty-9.3.7.v20160115 - 15 January 2016
|
||||
+ 471171 Support SYNC_FLUSH in GzipHandler
|
||||
+ 485469 permessage-deflate extension causes protocol error in Firefox/Chrome
|
||||
+ 485714 Update SSL configuration to mitigate SLOTH vulnerability
|
||||
|
|
|
@ -152,6 +152,9 @@ public abstract class PoolingHttpDestination<C extends Connection> extends HttpD
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Aborted before processing {}: {}", exchange, cause);
|
||||
// Won't use this connection, release it back.
|
||||
if (!connectionPool.release(connection))
|
||||
connection.close();
|
||||
// It may happen that the request is aborted before the exchange
|
||||
// is created. Aborting the exchange a second time will result in
|
||||
// a no-operation, so we just abort here to cover that edge case.
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.client.api.ContentResponse;
|
|||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.util.BasicAuthentication;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.B64Code;
|
||||
|
@ -157,4 +158,100 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(status, response3.getStatus());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticatedProxiedRequestWithRedirect() throws Exception
|
||||
{
|
||||
String user = "foo";
|
||||
String password = "bar";
|
||||
String credentials = B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1);
|
||||
String proxyHost = "localhost";
|
||||
String serverHost = "server";
|
||||
int serverPort = HttpScheme.HTTP.is(scheme) ? 80 : 443;
|
||||
String realm = "test_realm";
|
||||
int status = HttpStatus.NO_CONTENT_204;
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (target.startsWith("/proxy"))
|
||||
{
|
||||
String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
|
||||
if (authorization == null)
|
||||
{
|
||||
response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
|
||||
response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
String prefix = "Basic ";
|
||||
if (authorization.startsWith(prefix))
|
||||
{
|
||||
String attempt = authorization.substring(prefix.length());
|
||||
if (credentials.equals(attempt))
|
||||
{
|
||||
// Change also the host, to verify that proxy authentication works in this case too.
|
||||
response.sendRedirect(scheme + "://127.0.0.1:" + serverPort + "/server");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (target.startsWith("/server"))
|
||||
{
|
||||
response.setStatus(status);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR_500);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int proxyPort = connector.getLocalPort();
|
||||
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
|
||||
|
||||
ContentResponse response1 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.path("/proxy")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
// No Authentication available => 407.
|
||||
Assert.assertEquals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407, response1.getStatus());
|
||||
|
||||
// Add authentication...
|
||||
URI uri = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
|
||||
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, user, password));
|
||||
final AtomicInteger requests = new AtomicInteger();
|
||||
client.getRequestListeners().add(new Request.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Request request)
|
||||
{
|
||||
requests.incrementAndGet();
|
||||
}
|
||||
});
|
||||
// ...and perform the request again => 407 + 302 + 204.
|
||||
ContentResponse response2 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.path("/proxy")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(status, response2.getStatus());
|
||||
Assert.assertEquals(3, requests.get());
|
||||
|
||||
// Now the authentication result is cached => 204.
|
||||
requests.set(0);
|
||||
ContentResponse response3 = client.newRequest(serverHost, serverPort)
|
||||
.scheme(scheme)
|
||||
.path("/server")
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(status, response3.getStatus());
|
||||
Assert.assertEquals(1, requests.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ 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;
|
||||
|
@ -47,6 +48,33 @@ public class HttpRequestAbortTest extends AbstractHttpClientServerTest
|
|||
super(sslContextFactory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbortBeforeQueued() throws Exception
|
||||
{
|
||||
start(new EmptyServerHandler());
|
||||
|
||||
Exception failure = new Exception("oops");
|
||||
try
|
||||
{
|
||||
Request request = client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.timeout(5, TimeUnit.SECONDS);
|
||||
request.abort(failure);
|
||||
request.send();
|
||||
Assert.fail();
|
||||
}
|
||||
catch (ExecutionException x)
|
||||
{
|
||||
Assert.assertSame(failure, x.getCause());
|
||||
// Make sure the pool is in a sane state.
|
||||
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort());
|
||||
DuplexConnectionPool connectionPool = destination.getConnectionPool();
|
||||
Assert.assertEquals(1, connectionPool.getConnectionCount());
|
||||
Assert.assertEquals(0, connectionPool.getActiveConnections().size());
|
||||
Assert.assertEquals(1, connectionPool.getIdleConnections().size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAbortOnQueued() throws Exception
|
||||
{
|
||||
|
|
|
@ -89,7 +89,7 @@ public class ClientGenerator extends Generator
|
|||
beginRequestBuffer.putInt(0x00_08_00_00);
|
||||
// Hardcode RESPONDER role and KEEP_ALIVE flag
|
||||
beginRequestBuffer.putLong(0x00_01_01_00_00_00_00_00L);
|
||||
beginRequestBuffer.flip();
|
||||
BufferUtil.flipToFlush(beginRequestBuffer, 0);
|
||||
|
||||
int index = 0;
|
||||
while (fieldsLength > 0)
|
||||
|
@ -129,7 +129,7 @@ public class ClientGenerator extends Generator
|
|||
}
|
||||
|
||||
buffer.putShort(4, (short)length);
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -140,7 +140,7 @@ public class ClientGenerator extends Generator
|
|||
// Generate the last FCGI_PARAMS frame
|
||||
lastParamsBuffer.putInt(0x01_04_00_00 + request);
|
||||
lastParamsBuffer.putInt(0x00_00_00_00);
|
||||
lastParamsBuffer.flip();
|
||||
BufferUtil.flipToFlush(lastParamsBuffer, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public class Generator
|
|||
int length = Math.min(MAX_CONTENT_LENGTH, contentLength);
|
||||
buffer.putShort((short)length);
|
||||
buffer.putShort((short)0);
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
|
||||
if (contentLength == 0)
|
||||
break;
|
||||
|
|
|
@ -95,7 +95,7 @@ public class ServerGenerator extends Generator
|
|||
buffer.put(bytes.get(i)).put(COLON).put(bytes.get(i + 1)).put(EOL);
|
||||
buffer.put(EOL);
|
||||
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
|
||||
return generateContent(request, buffer, true, false, callback, FCGI.FrameType.STDOUT);
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ public class ServerGenerator extends Generator
|
|||
endRequestBuffer.putInt(0x00_08_00_00);
|
||||
endRequestBuffer.putInt(aborted ? 1 : 0);
|
||||
endRequestBuffer.putInt(0);
|
||||
endRequestBuffer.flip();
|
||||
BufferUtil.flipToFlush(endRequestBuffer, 0);
|
||||
return endRequestBuffer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<Arg>
|
||||
<Ref id="Server"/>
|
||||
</Arg>
|
||||
<Set name="workerName"><Property name="jetty.gcloudSession.workerName" default="node1"/></Set>
|
||||
<Set name="workerName"><Property name="jetty.gcloudSession.workerName"><Default>node<Env name="GAE_MODULE_INSTANCE" default="0"/></Default></Property></Set>
|
||||
<Set name="config"><Ref id="gconf"/></Set>
|
||||
</New>
|
||||
</Set>
|
||||
|
|
|
@ -53,7 +53,8 @@ https://github.com/GoogleCloudPlatform/gcloud-java
|
|||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
[ini-template]
|
||||
## Unique identifier for this node in the cluster
|
||||
## Unique identifier to force the workername for this node in the cluster
|
||||
## If not set, will default to the string "node" plus the Env variable $GAE_MODULE_INSTANCE
|
||||
# jetty.gcloudSession.workerName=node1
|
||||
|
||||
|
||||
|
|
|
@ -357,6 +357,7 @@ public class IdleTimeoutTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
dataLatch.countDown();
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ import org.eclipse.jetty.server.Connector;
|
|||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.util.ArrayQueue;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.junit.Assert;
|
||||
|
@ -168,12 +169,12 @@ public class PrefaceTest extends AbstractTest
|
|||
ByteBuffer buffer = byteBufferPool.acquire(1024, true);
|
||||
while (true)
|
||||
{
|
||||
BufferUtil.clearToFill(buffer);
|
||||
int read = socket.read(buffer);
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
if (read < 0)
|
||||
break;
|
||||
parser.parse(buffer);
|
||||
buffer.clear();
|
||||
}
|
||||
|
||||
Assert.assertEquals(2, settings.size());
|
||||
|
@ -248,9 +249,9 @@ public class PrefaceTest extends AbstractTest
|
|||
ByteBuffer buffer = byteBufferPool.acquire(1024, true);
|
||||
http1: while (true)
|
||||
{
|
||||
buffer.clear();
|
||||
BufferUtil.clearToFill(buffer);
|
||||
int read = socket.read(buffer);
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
if (read < 0)
|
||||
Assert.fail();
|
||||
|
||||
|
@ -314,9 +315,9 @@ public class PrefaceTest extends AbstractTest
|
|||
if (responded.get())
|
||||
break;
|
||||
|
||||
buffer.clear();
|
||||
BufferUtil.clearToFill(buffer);
|
||||
int read = socket.read(buffer);
|
||||
buffer.flip();
|
||||
BufferUtil.flipToFlush(buffer, 0);
|
||||
if (read < 0)
|
||||
Assert.fail();
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
warmupLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -188,6 +189,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
warmupLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -273,6 +275,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
warmupLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -298,6 +301,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
pushLatch.countDown();
|
||||
}
|
||||
};
|
||||
|
@ -325,6 +329,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
secondaryResponseLatch.countDown();
|
||||
}
|
||||
|
@ -372,6 +377,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
warmupLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -655,6 +661,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
warmupLatch.countDown();
|
||||
}
|
||||
|
@ -676,6 +683,7 @@ public class PushCacheFilterTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
if (frame.isEndStream())
|
||||
primaryResponseLatch.countDown();
|
||||
}
|
||||
|
|
|
@ -129,7 +129,12 @@ public class StreamCloseTest extends AbstractTest
|
|||
public void onData(final Stream stream, DataFrame frame, final Callback callback)
|
||||
{
|
||||
Assert.assertTrue(((HTTP2Stream)stream).isRemotelyClosed());
|
||||
stream.data(frame, new Callback()
|
||||
|
||||
// We must copy the data that we send asynchronously.
|
||||
ByteBuffer data = frame.getData();
|
||||
ByteBuffer copy = ByteBuffer.allocate(data.remaining());
|
||||
copy.put(data).flip();
|
||||
stream.data(new DataFrame(stream.getId(), copy, frame.isEndStream()), new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
|
@ -155,6 +160,7 @@ public class StreamCloseTest extends AbstractTest
|
|||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
// The sent data callback may not be notified yet here.
|
||||
callback.succeeded();
|
||||
completeLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -70,6 +70,10 @@ public class StreamCountTest extends AbstractTest
|
|||
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
|
||||
stream.headers(new HeadersFrame(stream.getId(), metaData, null, true), callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -144,6 +148,10 @@ public class StreamCountTest extends AbstractTest
|
|||
MetaData.Response metaData = new MetaData.Response(HttpVersion.HTTP_2, 200, fields);
|
||||
stream.headers(new HeadersFrame(stream.getId(), metaData, null, true), callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -171,6 +171,7 @@ public class StreamResetTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
stream1DataLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
@ -186,6 +187,7 @@ public class StreamResetTest extends AbstractTest
|
|||
@Override
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
stream2DataLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -46,9 +46,9 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
private final Map<IStream, Integer> streams = new HashMap<>();
|
||||
private final List<Entry> resets = new ArrayList<>();
|
||||
private final List<Entry> actives = new ArrayList<>();
|
||||
private final Queue<Entry> completes = new ArrayDeque<>();
|
||||
private final HTTP2Session session;
|
||||
private final ByteBufferPool.Lease lease;
|
||||
private boolean terminated;
|
||||
|
||||
public HTTP2Flusher(HTTP2Session session)
|
||||
{
|
||||
|
@ -58,57 +58,52 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
|
||||
public void window(IStream stream, WindowUpdateFrame frame)
|
||||
{
|
||||
boolean added = false;
|
||||
boolean closed;
|
||||
synchronized (this)
|
||||
{
|
||||
if (!isClosed())
|
||||
added = windows.offer(new WindowEntry(stream, frame));
|
||||
closed = terminated;
|
||||
if (!closed)
|
||||
windows.offer(new WindowEntry(stream, frame));
|
||||
}
|
||||
// Flush stalled data.
|
||||
if (added)
|
||||
if (!closed)
|
||||
iterate();
|
||||
}
|
||||
|
||||
public boolean prepend(Entry entry)
|
||||
{
|
||||
boolean fail = false;
|
||||
boolean closed;
|
||||
synchronized (this)
|
||||
{
|
||||
if (isClosed())
|
||||
{
|
||||
fail = true;
|
||||
}
|
||||
else
|
||||
closed = terminated;
|
||||
if (!closed)
|
||||
{
|
||||
frames.add(0, entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Prepended {}, frames={}", entry, frames.size());
|
||||
}
|
||||
}
|
||||
if (fail)
|
||||
if (closed)
|
||||
closed(entry, new ClosedChannelException());
|
||||
return !fail;
|
||||
return !closed;
|
||||
}
|
||||
|
||||
public boolean append(Entry entry)
|
||||
{
|
||||
boolean fail = false;
|
||||
boolean closed;
|
||||
synchronized (this)
|
||||
{
|
||||
if (isClosed())
|
||||
{
|
||||
fail = true;
|
||||
}
|
||||
else
|
||||
closed = terminated;
|
||||
if (!closed)
|
||||
{
|
||||
frames.offer(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Appended {}, frames={}", entry, frames.size());
|
||||
}
|
||||
}
|
||||
if (fail)
|
||||
if (closed)
|
||||
closed(entry, new ClosedChannelException());
|
||||
return !fail;
|
||||
return !closed;
|
||||
}
|
||||
|
||||
private Entry remove(int index)
|
||||
|
@ -138,6 +133,9 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
|
||||
synchronized (this)
|
||||
{
|
||||
if (terminated)
|
||||
throw new ClosedChannelException();
|
||||
|
||||
// First thing, update the window sizes, so we can
|
||||
// reason about the frames to remove from the queue.
|
||||
while (!windows.isEmpty())
|
||||
|
@ -226,12 +224,8 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
|
||||
if (actives.isEmpty())
|
||||
{
|
||||
if (isClosed())
|
||||
fail(new ClosedChannelException(), true);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Flushed {}", session);
|
||||
|
||||
return Action.IDLE;
|
||||
}
|
||||
|
||||
|
@ -259,20 +253,11 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
{
|
||||
lease.recycle();
|
||||
|
||||
// Transfer active items to avoid reentrancy.
|
||||
for (int i = 0; i < actives.size(); ++i)
|
||||
completes.add(actives.get(i));
|
||||
actives.clear();
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Written {} frames for {}", completes.size(), completes);
|
||||
LOG.debug("Written {} frames for {}", actives.size(), actives);
|
||||
|
||||
// Drain the frames one by one to avoid reentrancy.
|
||||
while (!completes.isEmpty())
|
||||
{
|
||||
Entry entry = completes.poll();
|
||||
entry.succeeded();
|
||||
}
|
||||
actives.forEach(Entry::succeeded);
|
||||
actives.clear();
|
||||
|
||||
super.succeeded();
|
||||
}
|
||||
|
@ -288,40 +273,40 @@ public class HTTP2Flusher extends IteratingCallback
|
|||
{
|
||||
lease.recycle();
|
||||
|
||||
// Transfer active items to avoid reentrancy.
|
||||
for (int i = 0; i < actives.size(); ++i)
|
||||
completes.add(actives.get(i));
|
||||
actives.clear();
|
||||
|
||||
// Drain the frames one by one to avoid reentrancy.
|
||||
while (!completes.isEmpty())
|
||||
{
|
||||
Entry entry = completes.poll();
|
||||
entry.failed(x);
|
||||
}
|
||||
|
||||
fail(x, isClosed());
|
||||
}
|
||||
|
||||
private void fail(Throwable x, boolean closed)
|
||||
{
|
||||
Queue<Entry> queued;
|
||||
boolean closed;
|
||||
synchronized (this)
|
||||
{
|
||||
queued = new ArrayDeque<>(frames);
|
||||
closed = terminated;
|
||||
terminated = true;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}, active/queued={}/{}", closed ? "Closing" : "Failing", actives.size(), frames.size());
|
||||
actives.addAll(frames);
|
||||
frames.clear();
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}, queued={}", closed ? "Closing" : "Failing", queued.size());
|
||||
|
||||
for (Entry entry : queued)
|
||||
entry.failed(x);
|
||||
actives.forEach(entry -> entry.failed(x));
|
||||
actives.clear();
|
||||
|
||||
// If the failure came from within the
|
||||
// flusher, we need to close the connection.
|
||||
if (!closed)
|
||||
session.abort(x);
|
||||
}
|
||||
|
||||
void terminate()
|
||||
{
|
||||
boolean closed;
|
||||
synchronized (this)
|
||||
{
|
||||
closed = terminated;
|
||||
terminated = true;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{}", closed ? "Terminated" : "Terminating");
|
||||
}
|
||||
if (!closed)
|
||||
iterate();
|
||||
}
|
||||
|
||||
private void closed(Entry entry, Throwable failure)
|
||||
{
|
||||
entry.failed(failure);
|
||||
|
|
|
@ -907,7 +907,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
{
|
||||
if (closed.compareAndSet(current, CloseState.CLOSED))
|
||||
{
|
||||
flusher.close();
|
||||
flusher.terminate();
|
||||
for (IStream stream : streams.values())
|
||||
stream.close();
|
||||
streams.clear();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.eclipse.jetty.http2.client.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.eclipse.jetty.client.HttpChannel;
|
||||
|
@ -34,6 +35,8 @@ import org.eclipse.jetty.http2.frames.DataFrame;
|
|||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
import org.eclipse.jetty.http2.frames.PushPromiseFrame;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
|
||||
public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listener
|
||||
|
@ -95,7 +98,42 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
|
|||
return;
|
||||
}
|
||||
|
||||
if (responseContent(exchange, frame.getData(), callback))
|
||||
// We must copy the data since we do not know when the
|
||||
// application will consume the bytes and the parsing
|
||||
// will continue as soon as this method returns, eventually
|
||||
// leading to reusing the underlying buffer for more reads.
|
||||
ByteBufferPool byteBufferPool = getHttpDestination().getHttpClient().getByteBufferPool();
|
||||
ByteBuffer original = frame.getData();
|
||||
int length = original.remaining();
|
||||
final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
|
||||
BufferUtil.clearToFill(copy);
|
||||
copy.put(original);
|
||||
BufferUtil.flipToFlush(copy, 0);
|
||||
|
||||
Callback delegate = new Callback()
|
||||
{
|
||||
@Override
|
||||
public boolean isNonBlocking()
|
||||
{
|
||||
return callback.isNonBlocking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
byteBufferPool.release(copy);
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
byteBufferPool.release(copy);
|
||||
callback.failed(x);
|
||||
}
|
||||
};
|
||||
|
||||
if (responseContent(exchange, copy, delegate))
|
||||
{
|
||||
if (frame.isEndStream())
|
||||
responseSuccess(exchange);
|
||||
|
|
|
@ -164,15 +164,17 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
|||
public Runnable requestContent(DataFrame frame, final Callback callback)
|
||||
{
|
||||
// We must copy the data since we do not know when the
|
||||
// application will consume its bytes (we queue them by
|
||||
// calling onContent()), and we cannot stop the parsing
|
||||
// since there may be frames for other streams.
|
||||
// application will consume the bytes (we queue them by
|
||||
// calling onContent()), and the parsing will continue
|
||||
// as soon as this method returns, eventually leading
|
||||
// to reusing the underlying buffer for more reads.
|
||||
final ByteBufferPool byteBufferPool = getByteBufferPool();
|
||||
ByteBuffer original = frame.getData();
|
||||
int length = original.remaining();
|
||||
final ByteBuffer copy = byteBufferPool.acquire(length, original.isDirect());
|
||||
BufferUtil.clearToFill(copy);
|
||||
copy.put(original).flip();
|
||||
copy.put(original);
|
||||
BufferUtil.flipToFlush(copy, 0);
|
||||
|
||||
boolean handle = onContent(new HttpInput.Content(copy)
|
||||
{
|
||||
|
|
|
@ -68,10 +68,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements ManagedSel
|
|||
return SelectChannelEndPoint.this.toString()+":runUpdateKey";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private abstract class RejectableRunnable implements Runnable,Rejectable
|
||||
{
|
||||
@Override
|
||||
@Override
|
||||
public void reject()
|
||||
{
|
||||
try
|
||||
|
@ -84,7 +84,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements ManagedSel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Runnable _runFillable = new RejectableRunnable()
|
||||
{
|
||||
@Override
|
||||
|
@ -118,8 +118,8 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements ManagedSel
|
|||
@Override
|
||||
public void run()
|
||||
{
|
||||
getFillInterest().fillable();
|
||||
getWriteFlusher().completeWrite();
|
||||
getFillInterest().fillable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,7 +175,7 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements ManagedSel
|
|||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onSelected {}->{} r={} w={} for {}", oldInterestOps, newInterestOps, readable, writable, this);
|
||||
|
||||
|
||||
// Run non-blocking code immediately.
|
||||
// This producer knows that this non-blocking code is special
|
||||
// and that it must be run in this thread and not fed to the
|
||||
|
|
|
@ -61,16 +61,16 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
Interceptor getNextInterceptor();
|
||||
boolean isOptimizedForDirectBuffers();
|
||||
}
|
||||
|
||||
|
||||
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
||||
|
||||
private final HttpChannel _channel;
|
||||
private final SharedBlockingCallback _writeBlock;
|
||||
private Interceptor _interceptor;
|
||||
|
||||
|
||||
/** Bytes written via the write API (excludes bytes written via sendContent). Used to autocommit once content length is written. */
|
||||
private long _written;
|
||||
|
||||
|
||||
private ByteBuffer _aggregate;
|
||||
private int _bufferSize;
|
||||
private int _commitSize;
|
||||
|
@ -115,7 +115,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_commitSize=_bufferSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public HttpChannel getHttpChannel()
|
||||
{
|
||||
return _channel;
|
||||
|
@ -130,7 +130,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
_interceptor=filter;
|
||||
}
|
||||
|
||||
|
||||
public boolean isWritten()
|
||||
{
|
||||
return _written > 0;
|
||||
|
@ -155,11 +155,11 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
return _writeBlock.acquire();
|
||||
}
|
||||
|
||||
|
||||
private void write(ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
try (Blocker blocker = _writeBlock.acquire())
|
||||
{
|
||||
{
|
||||
write(content, complete, blocker);
|
||||
blocker.block();
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
case ERROR:
|
||||
throw new EofException(_onError);
|
||||
|
||||
|
||||
case CLOSED:
|
||||
return;
|
||||
|
||||
|
@ -378,7 +378,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
case ERROR:
|
||||
throw new EofException(_onError);
|
||||
|
||||
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
|
||||
|
@ -480,7 +480,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
case ERROR:
|
||||
throw new EofException(_onError);
|
||||
|
||||
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
|
||||
|
@ -562,7 +562,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
case ERROR:
|
||||
throw new EofException(_onError);
|
||||
|
||||
|
||||
case CLOSED:
|
||||
throw new EofException("Closed");
|
||||
|
||||
|
@ -592,7 +592,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendContent({})",BufferUtil.toDetailString(content));
|
||||
|
||||
|
||||
write(content, true);
|
||||
closed();
|
||||
}
|
||||
|
@ -672,7 +672,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendContent(buffer={},{})",BufferUtil.toDetailString(content),callback);
|
||||
|
||||
|
||||
write(content, true, new Callback()
|
||||
{
|
||||
@Override
|
||||
|
@ -702,7 +702,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendContent(stream={},{})",in,callback);
|
||||
|
||||
|
||||
new InputStreamWritingCB(in, callback).iterate();
|
||||
}
|
||||
|
||||
|
@ -717,7 +717,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendContent(channel={},{})",in,callback);
|
||||
|
||||
|
||||
new ReadableByteChannelWritingCB(in, callback).iterate();
|
||||
}
|
||||
|
||||
|
@ -731,7 +731,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendContent(http={},{})",httpContent,callback);
|
||||
|
||||
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
callback.failed(new IOException("cannot sendContent() after write()"));
|
||||
|
@ -755,7 +755,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
case ERROR:
|
||||
callback.failed(new EofException(_onError));
|
||||
return;
|
||||
|
||||
|
||||
case CLOSED:
|
||||
callback.failed(new EofException("Closed"));
|
||||
return;
|
||||
|
@ -765,7 +765,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ByteBuffer buffer = _channel.useDirectBuffers() ? httpContent.getDirectBuffer() : null;
|
||||
if (buffer == null)
|
||||
|
@ -819,7 +819,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
resetBuffer();
|
||||
_interceptor=_channel;
|
||||
}
|
||||
|
||||
|
||||
public void resetBuffer()
|
||||
{
|
||||
_written = 0;
|
||||
|
@ -875,7 +875,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
case ERROR:
|
||||
return true;
|
||||
|
||||
|
||||
case CLOSED:
|
||||
return true;
|
||||
|
||||
|
@ -918,7 +918,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
switch(_state.get())
|
||||
{
|
||||
case CLOSED:
|
||||
|
@ -939,7 +939,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_onError = e;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
_onError=new IllegalStateException("state="+_state.get());
|
||||
}
|
||||
|
@ -1062,7 +1062,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
_slice=_buffer.duplicate();
|
||||
_buffer.position(_buffer.limit());
|
||||
}
|
||||
}
|
||||
_complete=complete;
|
||||
}
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
BufferUtil.flipToFlush(_aggregate, position);
|
||||
return Action.SUCCEEDED;
|
||||
}
|
||||
|
||||
|
||||
// Is there data left to write?
|
||||
if (_buffer.hasRemaining())
|
||||
{
|
||||
|
@ -1096,7 +1096,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
write(_buffer, _complete, this);
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
|
||||
// otherwise take a slice
|
||||
int p=_buffer.position();
|
||||
int l=Math.min(getBufferSize(),_buffer.remaining());
|
||||
|
@ -1108,7 +1108,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
write(_slice, _complete && _completed, this);
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
|
||||
// all content written, but if we have not yet signal completion, we
|
||||
// need to do so
|
||||
if (_complete && !_completed)
|
||||
|
@ -1168,7 +1168,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_channel.getByteBufferPool().release(_buffer);
|
||||
return Action.SUCCEEDED;
|
||||
}
|
||||
|
||||
|
||||
// Read until buffer full or EOF
|
||||
int len=0;
|
||||
while (len<_buffer.capacity() && !_eof)
|
||||
|
@ -1218,7 +1218,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_in=in;
|
||||
_buffer = _channel.getByteBufferPool().acquire(getBufferSize(), _channel.useDirectBuffers());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Action process() throws Exception
|
||||
{
|
||||
|
@ -1233,16 +1233,16 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_channel.getByteBufferPool().release(_buffer);
|
||||
return Action.SUCCEEDED;
|
||||
}
|
||||
|
||||
|
||||
// Read from stream until buffer full or EOF
|
||||
_buffer.clear();
|
||||
BufferUtil.clearToFill(_buffer);
|
||||
while (_buffer.hasRemaining() && !_eof)
|
||||
_eof = (_in.read(_buffer)) < 0;
|
||||
|
||||
// write what we have
|
||||
_buffer.flip();
|
||||
BufferUtil.flipToFlush(_buffer, 0);
|
||||
write(_buffer,_eof,this);
|
||||
|
||||
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
|
|
|
@ -167,7 +167,7 @@ public class Request implements HttpServletRequest
|
|||
private String _pathInfo;
|
||||
|
||||
private boolean _secure;
|
||||
private boolean _asyncSupported = true;
|
||||
private String _asyncNotSupportedSource = null;
|
||||
private boolean _newContext;
|
||||
private boolean _cookiesExtracted = false;
|
||||
private boolean _handled = false;
|
||||
|
@ -1653,7 +1653,7 @@ public class Request implements HttpServletRequest
|
|||
@Override
|
||||
public boolean isAsyncSupported()
|
||||
{
|
||||
return _asyncSupported;
|
||||
return _asyncNotSupportedSource==null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1828,7 +1828,7 @@ public class Request implements HttpServletRequest
|
|||
if (_async!=null)
|
||||
_async.reset();
|
||||
_async=null;
|
||||
_asyncSupported = true;
|
||||
_asyncNotSupportedSource = null;
|
||||
_handled = false;
|
||||
if (_attributes != null)
|
||||
_attributes.clearAttributes();
|
||||
|
@ -1898,9 +1898,9 @@ public class Request implements HttpServletRequest
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setAsyncSupported(boolean supported)
|
||||
public void setAsyncSupported(boolean supported,String source)
|
||||
{
|
||||
_asyncSupported = supported;
|
||||
_asyncNotSupportedSource = supported?null:(source==null?"unknown":source);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -2220,8 +2220,8 @@ public class Request implements HttpServletRequest
|
|||
@Override
|
||||
public AsyncContext startAsync() throws IllegalStateException
|
||||
{
|
||||
if (!_asyncSupported)
|
||||
throw new IllegalStateException("!asyncSupported");
|
||||
if (_asyncNotSupportedSource!=null)
|
||||
throw new IllegalStateException("!asyncSupported: "+_asyncNotSupportedSource);
|
||||
HttpChannelState state = getHttpChannelState();
|
||||
if (_async==null)
|
||||
_async=new AsyncContextState(state);
|
||||
|
@ -2234,8 +2234,8 @@ public class Request implements HttpServletRequest
|
|||
@Override
|
||||
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
|
||||
{
|
||||
if (!_asyncSupported)
|
||||
throw new IllegalStateException("!asyncSupported");
|
||||
if (_asyncNotSupportedSource!=null)
|
||||
throw new IllegalStateException("!asyncSupported: "+_asyncNotSupportedSource);
|
||||
HttpChannelState state = getHttpChannelState();
|
||||
if (_async==null)
|
||||
_async=new AsyncContextState(state);
|
||||
|
|
|
@ -1660,17 +1660,21 @@ public class ServletHandler extends ScopedHandler
|
|||
//if the request already does not support async, then the setting for the filter
|
||||
//is irrelevant. However if the request supports async but this filter does not
|
||||
//temporarily turn it off for the execution of the filter
|
||||
boolean requestAsyncSupported = baseRequest.isAsyncSupported();
|
||||
try
|
||||
{
|
||||
if (!_filterHolder.isAsyncSupported() && requestAsyncSupported)
|
||||
baseRequest.setAsyncSupported(false);
|
||||
if (baseRequest.isAsyncSupported() && !_filterHolder.isAsyncSupported())
|
||||
{
|
||||
try
|
||||
{
|
||||
baseRequest.setAsyncSupported(false,_filterHolder.toString());
|
||||
filter.doFilter(request, response, _next);
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseRequest.setAsyncSupported(true,null);
|
||||
}
|
||||
}
|
||||
else
|
||||
filter.doFilter(request, response, _next);
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseRequest.setAsyncSupported(requestAsyncSupported);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1733,17 +1737,21 @@ public class ServletHandler extends ScopedHandler
|
|||
//if the request already does not support async, then the setting for the filter
|
||||
//is irrelevant. However if the request supports async but this filter does not
|
||||
//temporarily turn it off for the execution of the filter
|
||||
boolean requestAsyncSupported = _baseRequest.isAsyncSupported();
|
||||
try
|
||||
if (!holder.isAsyncSupported() && _baseRequest.isAsyncSupported())
|
||||
{
|
||||
if (!holder.isAsyncSupported() && requestAsyncSupported)
|
||||
_baseRequest.setAsyncSupported(false);
|
||||
try
|
||||
{
|
||||
_baseRequest.setAsyncSupported(false,holder.toString());
|
||||
filter.doFilter(request, response, this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_baseRequest.setAsyncSupported(true,null);
|
||||
}
|
||||
}
|
||||
else
|
||||
filter.doFilter(request, response, this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_baseRequest.setAsyncSupported(requestAsyncSupported);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -829,10 +829,20 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
if (_identityService!=null)
|
||||
old_run_as=_identityService.setRunAs(baseRequest.getResolvedUserIdentity(),_runAsToken);
|
||||
|
||||
if (!isAsyncSupported())
|
||||
baseRequest.setAsyncSupported(false);
|
||||
|
||||
servlet.service(request,response);
|
||||
if (baseRequest.isAsyncSupported() && !isAsyncSupported())
|
||||
{
|
||||
try
|
||||
{
|
||||
baseRequest.setAsyncSupported(false,this.toString());
|
||||
servlet.service(request,response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
baseRequest.setAsyncSupported(true,null);
|
||||
}
|
||||
}
|
||||
else
|
||||
servlet.service(request,response);
|
||||
servlet_error=false;
|
||||
}
|
||||
catch(UnavailableException e)
|
||||
|
@ -842,8 +852,6 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
}
|
||||
finally
|
||||
{
|
||||
baseRequest.setAsyncSupported(suspendable);
|
||||
|
||||
// pop run-as role
|
||||
if (_identityService!=null)
|
||||
_identityService.unsetRunAs(old_run_as);
|
||||
|
|
|
@ -56,6 +56,7 @@ import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
|||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
|
@ -63,6 +64,7 @@ import org.junit.Before;
|
|||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.eclipse.jetty.util.log.Log.getLogger;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
@ -112,6 +114,9 @@ public class AsyncServletTest
|
|||
_servletHandler.addServletWithMapping(holder,"/path2/*");
|
||||
_servletHandler.addServletWithMapping(holder,"/p th3/*");
|
||||
_servletHandler.addServletWithMapping(new ServletHolder(new FwdServlet()),"/fwd/*");
|
||||
ServletHolder holder2=new ServletHolder("NoAsync",_servlet);
|
||||
holder2.setAsyncSupported(false);
|
||||
_servletHandler.addServletWithMapping(holder2,"/noasync/*");
|
||||
_server.start();
|
||||
_port=_connector.getLocalPort();
|
||||
__history.clear();
|
||||
|
@ -163,7 +168,45 @@ public class AsyncServletTest
|
|||
|
||||
assertContains("NORMAL",response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncNotSupportedNoAsync() throws Exception
|
||||
{
|
||||
_expectedCode="200 ";
|
||||
String response=process("noasync","",null);
|
||||
Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
|
||||
assertThat(__history,contains(
|
||||
"REQUEST /ctx/noasync/info",
|
||||
"initial"
|
||||
));
|
||||
|
||||
assertContains("NORMAL",response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsyncNotSupportedAsync() throws Exception
|
||||
{
|
||||
((StdErrLog)getLogger(ServletHandler.class)).setHideStacks(true);
|
||||
try
|
||||
{
|
||||
_expectedCode="500 ";
|
||||
String response=process("noasync","start=200",null);
|
||||
Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 500 "));
|
||||
assertThat(__history,contains(
|
||||
"REQUEST /ctx/noasync/info",
|
||||
"initial"
|
||||
));
|
||||
|
||||
assertContains("HTTP ERROR: 500",response);
|
||||
assertContains("!asyncSupported",response);
|
||||
assertContains("AsyncServletTest$AsyncServlet",response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
((StdErrLog)getLogger(ServletHandler.class)).setHideStacks(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStart() throws Exception
|
||||
{
|
||||
|
|
|
@ -19,22 +19,13 @@
|
|||
package org.eclipse.jetty.util;
|
||||
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
|
@ -44,6 +35,12 @@ import org.junit.Assert;
|
|||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BufferUtilTest
|
||||
{
|
||||
@Test
|
||||
|
@ -159,7 +156,7 @@ public class BufferUtilTest
|
|||
assertEquals(2,from.remaining());
|
||||
assertEquals("1234567890",BufferUtil.toString(to));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
|
@ -172,7 +169,7 @@ public class BufferUtilTest
|
|||
assertEquals("123",BufferUtil.toString(to));
|
||||
BufferUtil.append(to,from.array(),3,2);
|
||||
assertEquals("12345",BufferUtil.toString(to));
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
BufferUtil.append(to,from.array(),0,5);
|
||||
|
@ -181,7 +178,7 @@ public class BufferUtilTest
|
|||
catch(BufferOverflowException e)
|
||||
{}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testPutDirect() throws Exception
|
||||
|
@ -296,7 +293,7 @@ public class BufferUtilTest
|
|||
int capacity = BufferUtil.TEMP_BUFFER_SIZE*2+1024;
|
||||
testWriteToWithBufferThatDoesNotExposeArray(capacity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testEnsureCapacity() throws Exception
|
||||
|
@ -305,13 +302,13 @@ public class BufferUtilTest
|
|||
assertTrue(b==BufferUtil.ensureCapacity(b, 0));
|
||||
assertTrue(b==BufferUtil.ensureCapacity(b, 10));
|
||||
assertTrue(b==BufferUtil.ensureCapacity(b, b.capacity()));
|
||||
|
||||
|
||||
|
||||
ByteBuffer b1 = BufferUtil.ensureCapacity(b, 64);
|
||||
assertTrue(b!=b1);
|
||||
assertEquals(64, b1.capacity());
|
||||
assertEquals("Goodbye Cruel World", BufferUtil.toString(b1));
|
||||
|
||||
|
||||
b1.position(8);
|
||||
b1.limit(13);
|
||||
assertEquals("Cruel", BufferUtil.toString(b1));
|
||||
|
@ -328,9 +325,9 @@ public class BufferUtilTest
|
|||
assertEquals(64, b3.capacity());
|
||||
assertEquals("Cruel", BufferUtil.toString(b3));
|
||||
assertEquals(0, b3.arrayOffset());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void testWriteToWithBufferThatDoesNotExposeArray(int capacity) throws IOException
|
||||
{
|
||||
|
@ -342,7 +339,7 @@ public class BufferUtilTest
|
|||
BufferUtil.writeTo(buffer.asReadOnlyBuffer(), out);
|
||||
assertThat("Bytes in out equal bytes in buffer", Arrays.equals(bytes, out.toByteArray()), is(true));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testMappedFile() throws Exception
|
||||
{
|
||||
|
@ -353,26 +350,26 @@ public class BufferUtilTest
|
|||
{
|
||||
out.write(data);
|
||||
}
|
||||
|
||||
|
||||
ByteBuffer mapped = BufferUtil.toMappedBuffer(file);
|
||||
assertEquals(data,BufferUtil.toString(mapped));
|
||||
assertTrue(BufferUtil.isMappedBuffer(mapped));
|
||||
|
||||
|
||||
ByteBuffer direct = BufferUtil.allocateDirect(data.length());
|
||||
direct.clear();
|
||||
BufferUtil.clearToFill(direct);
|
||||
direct.put(data.getBytes(StandardCharsets.ISO_8859_1));
|
||||
direct.flip();
|
||||
BufferUtil.flipToFlush(direct, 0);
|
||||
assertEquals(data,BufferUtil.toString(direct));
|
||||
assertFalse(BufferUtil.isMappedBuffer(direct));
|
||||
|
||||
|
||||
ByteBuffer slice = direct.slice();
|
||||
assertEquals(data,BufferUtil.toString(slice));
|
||||
assertFalse(BufferUtil.isMappedBuffer(slice));
|
||||
|
||||
|
||||
ByteBuffer duplicate = direct.duplicate();
|
||||
assertEquals(data,BufferUtil.toString(duplicate));
|
||||
assertFalse(BufferUtil.isMappedBuffer(duplicate));
|
||||
|
||||
|
||||
ByteBuffer readonly = direct.asReadOnlyBuffer();
|
||||
assertEquals(data,BufferUtil.toString(readonly));
|
||||
assertFalse(BufferUtil.isMappedBuffer(readonly));
|
||||
|
|
Loading…
Reference in New Issue