Merge remote-tracking branch 'origin/jetty-9.3.x' into jetty-9.4.x
This commit is contained in:
commit
0a2da4822d
|
@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionException;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.eclipse.jetty.client.api.ContentProvider;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
|
@ -81,6 +82,7 @@ public class HttpRequest implements Request
|
|||
private List<HttpCookie> cookies;
|
||||
private Map<String, Object> attributes;
|
||||
private List<RequestListener> requestListeners;
|
||||
private BiFunction<Request, Request, Response.CompleteListener> pushListener;
|
||||
|
||||
protected HttpRequest(HttpClient client, HttpConversation conversation, URI uri)
|
||||
{
|
||||
|
@ -567,6 +569,26 @@ public class HttpRequest implements Request
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets a listener for pushed resources.</p>
|
||||
* <p>When resources are pushed from the server, the given {@code listener}
|
||||
* is invoked for every pushed resource.
|
||||
* The parameters to the {@code BiFunction} are this request and the
|
||||
* synthesized request for the pushed resource.
|
||||
* The {@code BiFunction} should return a {@code CompleteListener} that
|
||||
* may also implement other listener interfaces to be notified of various
|
||||
* response events, or {@code null} to signal that the pushed resource
|
||||
* should be canceled.</p>
|
||||
*
|
||||
* @param listener a listener for pushed resource events
|
||||
* @return this request object
|
||||
*/
|
||||
public Request pushListener(BiFunction<Request, Request, Response.CompleteListener> listener)
|
||||
{
|
||||
this.pushListener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentProvider getContent()
|
||||
{
|
||||
|
@ -698,6 +720,11 @@ public class HttpRequest implements Request
|
|||
return responseListeners;
|
||||
}
|
||||
|
||||
public BiFunction<Request, Request, Response.CompleteListener> getPushListener()
|
||||
{
|
||||
return pushListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean abort(Throwable cause)
|
||||
{
|
||||
|
|
|
@ -36,7 +36,7 @@ At its most basic, you configure Jetty from two elements:
|
|||
|
||||
Instead of editing these directly, Jetty 9.1 introduced more options on how to configure Jetty (these are merely syntactic sugar that eventually resolve into the two basic configuration components).
|
||||
|
||||
Jetty 9.1 Startup Features include:
|
||||
Jetty Startup Features include:
|
||||
|
||||
* A separation of the Jetty distribution binaries in `${jetty.home}` and the environment specific configurations (and binaries) found in `${jetty.base}` (detailed in link:#startup-jetty-base-and-jetty-home[Managing Jetty Base and Jetty Home.])
|
||||
* You can enable a set of libraries and XML configuration files via the newly introduced link:#startup-modules[module system.]
|
||||
|
|
|
@ -40,6 +40,20 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
private final static Double ZERO=new Double(0.0);
|
||||
private final static Double ONE=new Double(1.0);
|
||||
|
||||
|
||||
/**
|
||||
* Function to apply a most specific MIME encoding secondary ordering
|
||||
*/
|
||||
public static Function<String, Integer> MOST_SPECIFIC = new Function<String, Integer>()
|
||||
{
|
||||
@Override
|
||||
public Integer apply(String s)
|
||||
{
|
||||
String[] elements = s.split("/");
|
||||
return 1000000*elements.length+1000*elements[0].length()+elements[elements.length-1].length();
|
||||
}
|
||||
};
|
||||
|
||||
private final List<Double> _quality = new ArrayList<>();
|
||||
private boolean _sorted = false;
|
||||
private final Function<String, Integer> _secondaryOrdering;
|
||||
|
@ -50,7 +64,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
*/
|
||||
public QuotedQualityCSV()
|
||||
{
|
||||
this((s) -> s.length());
|
||||
this((s) -> 0);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -101,7 +115,14 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
@Override
|
||||
protected void parsedParam(StringBuffer buffer, int valueLength, int paramName, int paramValue)
|
||||
{
|
||||
if (buffer.charAt(paramName)=='q' && paramValue>paramName && buffer.charAt(paramName+1)=='=')
|
||||
if (paramName<0)
|
||||
{
|
||||
if (buffer.charAt(buffer.length()-1)==';')
|
||||
buffer.setLength(buffer.length()-1);
|
||||
}
|
||||
if (paramValue>=0 &&
|
||||
buffer.charAt(paramName)=='q' && paramValue>paramName &&
|
||||
buffer.length()>=paramName && buffer.charAt(paramName+1)=='=')
|
||||
{
|
||||
Double q;
|
||||
try
|
||||
|
@ -142,7 +163,7 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
_sorted=true;
|
||||
|
||||
Double last = ZERO;
|
||||
int lastOrderIndex = Integer.MIN_VALUE;
|
||||
int lastSecondaryOrder = Integer.MIN_VALUE;
|
||||
|
||||
for (int i = _values.size(); i-- > 0;)
|
||||
{
|
||||
|
@ -150,20 +171,20 @@ public class QuotedQualityCSV extends QuotedCSV implements Iterable<String>
|
|||
Double q = _quality.get(i);
|
||||
|
||||
int compare=last.compareTo(q);
|
||||
if (compare>0 || (compare==0 && _secondaryOrdering.apply(v)<lastOrderIndex))
|
||||
if (compare>0 || (compare==0 && _secondaryOrdering.apply(v)<lastSecondaryOrder))
|
||||
{
|
||||
_values.set(i, _values.get(i + 1));
|
||||
_values.set(i + 1, v);
|
||||
_quality.set(i, _quality.get(i + 1));
|
||||
_quality.set(i + 1, q);
|
||||
last = ZERO;
|
||||
lastOrderIndex=0;
|
||||
lastSecondaryOrder=0;
|
||||
i = _values.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
last=q;
|
||||
lastOrderIndex=_secondaryOrdering.apply(v);
|
||||
lastSecondaryOrder=_secondaryOrdering.apply(v);
|
||||
}
|
||||
|
||||
int last_element=_quality.size();
|
||||
|
|
|
@ -433,15 +433,41 @@ public class HttpFieldsTest
|
|||
fields.add("name", "nothing;q=0");
|
||||
fields.add("name", "one;q=0.4");
|
||||
fields.add("name", "three;x=y;q=0.2;a=b,two;q=0.3");
|
||||
fields.add("name", "first;");
|
||||
|
||||
|
||||
List<String> list = fields.getQualityCSV("name");
|
||||
assertEquals("zero",HttpFields.valueParameters(list.get(0),null));
|
||||
assertEquals("one",HttpFields.valueParameters(list.get(1),null));
|
||||
assertEquals("two",HttpFields.valueParameters(list.get(2),null));
|
||||
assertEquals("three",HttpFields.valueParameters(list.get(3),null));
|
||||
assertEquals("four",HttpFields.valueParameters(list.get(4),null));
|
||||
assertEquals("first",HttpFields.valueParameters(list.get(0),null));
|
||||
assertEquals("zero",HttpFields.valueParameters(list.get(1),null));
|
||||
assertEquals("one",HttpFields.valueParameters(list.get(2),null));
|
||||
assertEquals("two",HttpFields.valueParameters(list.get(3),null));
|
||||
assertEquals("three",HttpFields.valueParameters(list.get(4),null));
|
||||
assertEquals("four",HttpFields.valueParameters(list.get(5),null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetQualityCSVHeader() throws Exception
|
||||
{
|
||||
HttpFields fields = new HttpFields();
|
||||
|
||||
fields.put("some", "value");
|
||||
fields.add("Accept", "zero;q=0.9,four;q=0.1");
|
||||
fields.put("other", "value");
|
||||
fields.add("Accept", "nothing;q=0");
|
||||
fields.add("Accept", "one;q=0.4");
|
||||
fields.add("Accept", "three;x=y;q=0.2;a=b,two;q=0.3");
|
||||
fields.add("Accept", "first;");
|
||||
|
||||
|
||||
List<String> list = fields.getQualityCSV(HttpHeader.ACCEPT);
|
||||
assertEquals("first",HttpFields.valueParameters(list.get(0),null));
|
||||
assertEquals("zero",HttpFields.valueParameters(list.get(1),null));
|
||||
assertEquals("one",HttpFields.valueParameters(list.get(2),null));
|
||||
assertEquals("two",HttpFields.valueParameters(list.get(3),null));
|
||||
assertEquals("three",HttpFields.valueParameters(list.get(4),null));
|
||||
assertEquals("four",HttpFields.valueParameters(list.get(5),null));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDateFields() throws Exception
|
||||
|
|
|
@ -50,6 +50,17 @@ public class QuotedQualityCSVTest
|
|||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
|
||||
|
||||
// Note this sort is only on quality and not the most specific type as per 5.3.2
|
||||
Assert.assertThat(values,Matchers.contains("text/*","text/plain","text/plain;format=flowed","*/*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7231_5_3_2_example3_most_specific()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV(QuotedQualityCSV.MOST_SPECIFIC);
|
||||
values.addValue("text/*, text/plain, text/plain;format=flowed, */*");
|
||||
|
||||
Assert.assertThat(values,Matchers.contains("text/plain;format=flowed","text/plain","text/*","*/*"));
|
||||
}
|
||||
|
||||
|
@ -81,9 +92,9 @@ public class QuotedQualityCSVTest
|
|||
Assert.assertThat(values,Matchers.contains(
|
||||
"compress",
|
||||
"gzip",
|
||||
"gzip",
|
||||
"gzip",
|
||||
"*",
|
||||
"gzip",
|
||||
"gzip",
|
||||
"compress",
|
||||
"identity"
|
||||
));
|
||||
|
@ -217,4 +228,22 @@ public class QuotedQualityCSVTest
|
|||
values.addValue("gzip, *");
|
||||
assertThat(values, contains("*", "gzip"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSameQuality()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("one;q=0.5,two;q=0.5,three;q=0.5");
|
||||
Assert.assertThat(values.getValues(),Matchers.contains("one","two","three"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoQuality()
|
||||
{
|
||||
QuotedQualityCSV values = new QuotedQualityCSV();
|
||||
values.addValue("one,two;,three;x=y");
|
||||
Assert.assertThat(values.getValues(),Matchers.contains("one","two","three;x=y"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,19 +34,26 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
|||
{
|
||||
private final HttpConnectionOverHTTP2 connection;
|
||||
private final Session session;
|
||||
private final boolean push;
|
||||
private final HttpSenderOverHTTP2 sender;
|
||||
private final HttpReceiverOverHTTP2 receiver;
|
||||
private Stream stream;
|
||||
|
||||
public HttpChannelOverHTTP2(HttpDestination destination, HttpConnectionOverHTTP2 connection, Session session)
|
||||
public HttpChannelOverHTTP2(HttpDestination destination, HttpConnectionOverHTTP2 connection, Session session, boolean push)
|
||||
{
|
||||
super(destination);
|
||||
this.connection = connection;
|
||||
this.session = session;
|
||||
this.push = push;
|
||||
this.sender = new HttpSenderOverHTTP2(this);
|
||||
this.receiver = new HttpReceiverOverHTTP2(this);
|
||||
}
|
||||
|
||||
protected HttpConnectionOverHTTP2 getHttpConnection()
|
||||
{
|
||||
return connection;
|
||||
}
|
||||
|
||||
public Session getSession()
|
||||
{
|
||||
return session;
|
||||
|
@ -110,6 +117,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
|||
public void exchangeTerminated(HttpExchange exchange, Result result)
|
||||
{
|
||||
super.exchangeTerminated(exchange, result);
|
||||
release();
|
||||
if (!push)
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,15 +61,15 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
|
|||
normalizeRequest(exchange.getRequest());
|
||||
|
||||
// One connection maps to N channels, so for each exchange we create a new channel.
|
||||
HttpChannel channel = newHttpChannel();
|
||||
HttpChannel channel = newHttpChannel(false);
|
||||
channels.add(channel);
|
||||
|
||||
return send(channel, exchange);
|
||||
}
|
||||
|
||||
protected HttpChannelOverHTTP2 newHttpChannel()
|
||||
protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
|
||||
{
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession());
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push);
|
||||
}
|
||||
|
||||
protected void release(HttpChannel channel)
|
||||
|
|
|
@ -21,13 +21,19 @@ package org.eclipse.jetty.http2.client.http;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.eclipse.jetty.client.HttpChannel;
|
||||
import org.eclipse.jetty.client.HttpExchange;
|
||||
import org.eclipse.jetty.client.HttpReceiver;
|
||||
import org.eclipse.jetty.client.HttpRequest;
|
||||
import org.eclipse.jetty.client.HttpResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.client.api.Response;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
|
@ -91,7 +97,32 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
|
|||
@Override
|
||||
public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
|
||||
{
|
||||
// Not supported.
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
return null;
|
||||
|
||||
HttpRequest request = exchange.getRequest();
|
||||
MetaData.Request metaData = (MetaData.Request)frame.getMetaData();
|
||||
HttpRequest pushRequest = (HttpRequest)getHttpDestination().getHttpClient().newRequest(metaData.getURIString());
|
||||
|
||||
BiFunction<Request, Request, Response.CompleteListener> pushListener = request.getPushListener();
|
||||
if (pushListener != null)
|
||||
{
|
||||
Response.CompleteListener listener = pushListener.apply(request, pushRequest);
|
||||
if (listener != null)
|
||||
{
|
||||
HttpChannelOverHTTP2 pushChannel = getHttpChannel().getHttpConnection().newHttpChannel(true);
|
||||
List<Response.ResponseListener> listeners = Collections.singletonList(listener);
|
||||
HttpExchange pushExchange = new HttpExchange(getHttpDestination(), pushRequest, listeners);
|
||||
pushChannel.associate(pushExchange);
|
||||
pushChannel.setStream(stream);
|
||||
// TODO: idle timeout ?
|
||||
pushExchange.requestComplete(null);
|
||||
pushExchange.terminateRequest();
|
||||
return pushChannel.getStreamListener();
|
||||
}
|
||||
}
|
||||
|
||||
stream.reset(new ResetFrame(stream.getId(), ErrorCode.REFUSED_STREAM_ERROR.code), Callback.NOOP);
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -247,9 +247,9 @@ public class HttpClientTransportOverHTTP2Test extends AbstractTest
|
|||
return new HttpConnectionOverHTTP2(destination, session)
|
||||
{
|
||||
@Override
|
||||
protected HttpChannelOverHTTP2 newHttpChannel()
|
||||
protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
|
||||
{
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession())
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push)
|
||||
{
|
||||
@Override
|
||||
public void setStream(Stream stream)
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.http2.client.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.HttpRequest;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpURI;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.api.server.ServerSessionListener;
|
||||
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.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Promise;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PushedResourcesTest extends AbstractTest
|
||||
{
|
||||
@Test
|
||||
public void testPushedResourceCancelled() throws Exception
|
||||
{
|
||||
String pushPath = "/secondary";
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
start(new ServerSessionListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public Stream.Listener onNewStream(Stream stream, HeadersFrame frame)
|
||||
{
|
||||
HttpURI pushURI = new HttpURI("http://localhost:" + connector.getLocalPort() + pushPath);
|
||||
MetaData.Request pushRequest = new MetaData.Request(HttpMethod.GET.asString(), pushURI, HttpVersion.HTTP_2, new HttpFields());
|
||||
stream.push(new PushPromiseFrame(stream.getId(), 0, pushRequest), new Promise.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void succeeded(Stream pushStream)
|
||||
{
|
||||
// Just send the normal response and wait for the reset.
|
||||
MetaData.Response response = new MetaData.Response(HttpVersion.HTTP_2, HttpStatus.OK_200, new HttpFields());
|
||||
stream.headers(new HeadersFrame(stream.getId(), response, null, true), Callback.NOOP);
|
||||
}
|
||||
}, new Stream.Listener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReset(Stream stream, ResetFrame frame)
|
||||
{
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
|
||||
ContentResponse response = request
|
||||
.pushListener((mainRequest, pushedRequest) -> null)
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushedResources() throws Exception
|
||||
{
|
||||
Random random = new Random();
|
||||
byte[] bytes = new byte[512];
|
||||
random.nextBytes(bytes);
|
||||
byte[] pushBytes1 = new byte[1024];
|
||||
random.nextBytes(pushBytes1);
|
||||
byte[] pushBytes2 = new byte[2048];
|
||||
random.nextBytes(pushBytes2);
|
||||
|
||||
String path1 = "/secondary1";
|
||||
String path2 = "/secondary2";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (target.equals(path1))
|
||||
{
|
||||
response.getOutputStream().write(pushBytes1);
|
||||
}
|
||||
else if (target.equals(path2))
|
||||
{
|
||||
response.getOutputStream().write(pushBytes2);
|
||||
}
|
||||
else
|
||||
{
|
||||
baseRequest.getPushBuilder()
|
||||
.path(path1)
|
||||
.push();
|
||||
baseRequest.getPushBuilder()
|
||||
.path(path2)
|
||||
.push();
|
||||
response.getOutputStream().write(bytes);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch latch1 = new CountDownLatch(1);
|
||||
CountDownLatch latch2 = new CountDownLatch(1);
|
||||
HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
|
||||
ContentResponse response = request
|
||||
.pushListener((mainRequest, pushedRequest) -> new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isSucceeded());
|
||||
if (pushedRequest.getPath().equals(path1))
|
||||
{
|
||||
Assert.assertArrayEquals(pushBytes1, getContent());
|
||||
latch1.countDown();
|
||||
}
|
||||
else if (pushedRequest.getPath().equals(path2))
|
||||
{
|
||||
Assert.assertArrayEquals(pushBytes2, getContent());
|
||||
latch2.countDown();
|
||||
}
|
||||
}
|
||||
})
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
Assert.assertArrayEquals(bytes, response.getContent());
|
||||
Assert.assertTrue(latch1.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(latch2.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPushedResourceRedirect() throws Exception
|
||||
{
|
||||
Random random = new Random();
|
||||
byte[] pushBytes = new byte[512];
|
||||
random.nextBytes(pushBytes);
|
||||
|
||||
String oldPath = "/old";
|
||||
String newPath = "/new";
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
if (target.equals(oldPath))
|
||||
response.sendRedirect(newPath);
|
||||
else if (target.equals(newPath))
|
||||
response.getOutputStream().write(pushBytes);
|
||||
else
|
||||
baseRequest.getPushBuilder().path(oldPath).push();
|
||||
}
|
||||
});
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
HttpRequest request = (HttpRequest)client.newRequest("localhost", connector.getLocalPort());
|
||||
ContentResponse response = request
|
||||
.pushListener((mainRequest, pushedRequest) -> new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
public void onComplete(Result result)
|
||||
{
|
||||
Assert.assertTrue(result.isSucceeded());
|
||||
Assert.assertEquals(oldPath, pushedRequest.getPath());
|
||||
Assert.assertEquals(newPath, result.getRequest().getPath());
|
||||
Assert.assertArrayEquals(pushBytes, getContent());
|
||||
latch.countDown();
|
||||
}
|
||||
})
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -111,7 +111,6 @@ public class HttpChannelState
|
|||
{
|
||||
_interested = interest;
|
||||
}
|
||||
|
||||
private boolean isInterested() { return _interested;}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.io.FilterOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
|
@ -176,20 +177,43 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
|
||||
_rollTask=new RollTask();
|
||||
|
||||
midnight = ZonedDateTime.now().toLocalDate().atStartOfDay(zone.toZoneId());
|
||||
midnight = toMidnight(ZonedDateTime.now(), zone.toZoneId());
|
||||
|
||||
scheduleNextRollover();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleNextRollover()
|
||||
/**
|
||||
* Get the "start of day" for the provided DateTime at the zone specified.
|
||||
*
|
||||
* @param dateTime the date time to calculate from
|
||||
* @param zone the zone to return the date in
|
||||
* @return start of the day of the date provided
|
||||
*/
|
||||
public static ZonedDateTime toMidnight(ZonedDateTime dateTime, ZoneId zone)
|
||||
{
|
||||
return dateTime.toLocalDate().atStartOfDay(zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next "start of day" for the provided date.
|
||||
*
|
||||
* @param dateTime the date to calculate from
|
||||
* @return the start of the next day
|
||||
*/
|
||||
public static ZonedDateTime nextMidnight(ZonedDateTime dateTime)
|
||||
{
|
||||
// Increment to next day.
|
||||
// Using Calendar.add(DAY, 1) takes in account Daylights Savings
|
||||
// differences, and still maintains the "midnight" settings for
|
||||
// Hour, Minute, Second, Milliseconds
|
||||
midnight = midnight.toLocalDate().plus(1, ChronoUnit.DAYS).atStartOfDay(midnight.getZone());
|
||||
__rollover.schedule(_rollTask,midnight.toInstant().toEpochMilli());
|
||||
return dateTime.toLocalDate().plus(1, ChronoUnit.DAYS).atStartOfDay(dateTime.getZone());
|
||||
}
|
||||
|
||||
private void scheduleNextRollover()
|
||||
{
|
||||
midnight = nextMidnight(midnight);
|
||||
__rollover.schedule(_rollTask,midnight.toInstant().toEpochMilli() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.util;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class RolloverFileOutputStreamTest
|
||||
{
|
||||
private static ZoneId toZoneId(String timezoneId)
|
||||
{
|
||||
ZoneId zone = TimeZone.getTimeZone(timezoneId).toZoneId();
|
||||
// System.out.printf(".toZoneId(\"%s\") = [id=%s,normalized=%s]%n", timezoneId, zone.getId(), zone.normalized());
|
||||
return zone;
|
||||
}
|
||||
|
||||
private static ZonedDateTime toDateTime(String timendate, ZoneId zone)
|
||||
{
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z")
|
||||
.withZone(zone);
|
||||
return ZonedDateTime.parse(timendate, formatter);
|
||||
}
|
||||
|
||||
private static String toString(TemporalAccessor date)
|
||||
{
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd-hh:mm:ss.S a z");
|
||||
return formatter.format(date);
|
||||
}
|
||||
|
||||
private void assertSequence(ZonedDateTime midnight, Object[][] expected)
|
||||
{
|
||||
ZonedDateTime nextEvent = midnight;
|
||||
|
||||
for (int i = 0; i < expected.length; i++)
|
||||
{
|
||||
long lastMs = nextEvent.toInstant().toEpochMilli();
|
||||
nextEvent = RolloverFileOutputStream.nextMidnight(nextEvent);
|
||||
assertThat("Next Event", toString(nextEvent), is(expected[i][0]));
|
||||
long duration = (nextEvent.toInstant().toEpochMilli() - lastMs);
|
||||
assertThat("Duration to next event", duration, is((long) expected[i][1]));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMidnightRolloverCalc_PST_DST_Start()
|
||||
{
|
||||
ZoneId zone = toZoneId("PST");
|
||||
ZonedDateTime initialDate = toDateTime("2016.03.11-01:23:45.0 PM PST", zone);
|
||||
|
||||
ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone);
|
||||
assertThat("Midnight", toString(midnight), is("2016.03.11-12:00:00.0 AM PST"));
|
||||
|
||||
Object expected[][] = {
|
||||
{"2016.03.12-12:00:00.0 AM PST", 86_400_000L},
|
||||
{"2016.03.13-12:00:00.0 AM PST", 86_400_000L},
|
||||
{"2016.03.14-12:00:00.0 AM PDT", 82_800_000L}, // the short day
|
||||
{"2016.03.15-12:00:00.0 AM PDT", 86_400_000L},
|
||||
{"2016.03.16-12:00:00.0 AM PDT", 86_400_000L},
|
||||
};
|
||||
|
||||
assertSequence(midnight, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMidnightRolloverCalc_PST_DST_End()
|
||||
{
|
||||
ZoneId zone = toZoneId("PST");
|
||||
ZonedDateTime initialDate = toDateTime("2016.11.04-11:22:33.0 AM PDT", zone);
|
||||
|
||||
ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone);
|
||||
assertThat("Midnight", toString(midnight), is("2016.11.04-12:00:00.0 AM PDT"));
|
||||
|
||||
Object expected[][] = {
|
||||
{"2016.11.05-12:00:00.0 AM PDT", 86_400_000L},
|
||||
{"2016.11.06-12:00:00.0 AM PDT", 86_400_000L},
|
||||
{"2016.11.07-12:00:00.0 AM PST", 90_000_000L}, // the long day
|
||||
{"2016.11.08-12:00:00.0 AM PST", 86_400_000L},
|
||||
{"2016.11.09-12:00:00.0 AM PST", 86_400_000L},
|
||||
};
|
||||
|
||||
assertSequence(midnight, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMidnightRolloverCalc_Sydney_DST_Start()
|
||||
{
|
||||
ZoneId zone = toZoneId("Australia/Sydney");
|
||||
ZonedDateTime initialDate = toDateTime("2016.10.01-01:23:45.0 PM AEST", zone);
|
||||
|
||||
ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone);
|
||||
assertThat("Midnight", toString(midnight), is("2016.10.01-12:00:00.0 AM AEST"));
|
||||
|
||||
Object expected[][] = {
|
||||
{"2016.10.02-12:00:00.0 AM AEST", 86_400_000L},
|
||||
{"2016.10.03-12:00:00.0 AM AEDT", 82_800_000L}, // the short day
|
||||
{"2016.10.04-12:00:00.0 AM AEDT", 86_400_000L},
|
||||
{"2016.10.05-12:00:00.0 AM AEDT", 86_400_000L},
|
||||
{"2016.10.06-12:00:00.0 AM AEDT", 86_400_000L},
|
||||
};
|
||||
|
||||
assertSequence(midnight, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMidnightRolloverCalc_Sydney_DST_End()
|
||||
{
|
||||
ZoneId zone = toZoneId("Australia/Sydney");
|
||||
ZonedDateTime initialDate = toDateTime("2016.04.02-11:22:33.0 AM AEDT", zone);
|
||||
|
||||
ZonedDateTime midnight = RolloverFileOutputStream.toMidnight(initialDate, zone);
|
||||
assertThat("Midnight", toString(midnight), is("2016.04.02-12:00:00.0 AM AEDT"));
|
||||
|
||||
Object expected[][] = {
|
||||
{"2016.04.03-12:00:00.0 AM AEDT", 86_400_000L},
|
||||
{"2016.04.04-12:00:00.0 AM AEST", 90_000_000L}, // The long day
|
||||
{"2016.04.05-12:00:00.0 AM AEST", 86_400_000L},
|
||||
{"2016.04.06-12:00:00.0 AM AEST", 86_400_000L},
|
||||
{"2016.04.07-12:00:00.0 AM AEST", 86_400_000L},
|
||||
};
|
||||
|
||||
assertSequence(midnight, expected);
|
||||
}
|
||||
}
|
|
@ -21,14 +21,15 @@ package org.eclipse.jetty.websocket.client;
|
|||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
|
@ -40,6 +41,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
|
@ -72,9 +74,7 @@ import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
|
|||
import org.eclipse.jetty.websocket.common.test.RawFrameBuilder;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -91,14 +91,15 @@ public class ClientCloseTest
|
|||
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
public AtomicInteger closeCount = new AtomicInteger(0);
|
||||
public CountDownLatch openLatch = new CountDownLatch(1);
|
||||
public CountDownLatch errorLatch = new CountDownLatch(1);
|
||||
|
||||
public EventQueue<String> messageQueue = new EventQueue<>();
|
||||
public EventQueue<Throwable> errorQueue = new EventQueue<>();
|
||||
public AtomicReference<Throwable> error = new AtomicReference<>();
|
||||
|
||||
public void assertNoCloseEvent()
|
||||
{
|
||||
Assert.assertThat("Client Close Event",closeLatch.getCount(),is(1L));
|
||||
Assert.assertThat("Client Close Event Status Code ",closeCode,is(-1));
|
||||
assertThat("Client Close Event",closeLatch.getCount(),is(1L));
|
||||
assertThat("Client Close Event Status Code ",closeCode,is(-1));
|
||||
}
|
||||
|
||||
public void assertReceivedCloseEvent(int clientTimeoutMs, Matcher<Integer> statusCodeMatcher, Matcher<String> reasonMatcher)
|
||||
|
@ -106,39 +107,22 @@ public class ClientCloseTest
|
|||
{
|
||||
long maxTimeout = clientTimeoutMs * 2;
|
||||
|
||||
Assert.assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
|
||||
Assert.assertThat("Client Close Event Count",closeCount.get(),is(1));
|
||||
Assert.assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
|
||||
assertThat("Client Close Event Occurred",closeLatch.await(maxTimeout,TimeUnit.MILLISECONDS),is(true));
|
||||
assertThat("Client Close Event Count",closeCount.get(),is(1));
|
||||
assertThat("Client Close Event Status Code",closeCode,statusCodeMatcher);
|
||||
if (reasonMatcher == null)
|
||||
{
|
||||
Assert.assertThat("Client Close Event Reason",closeReason,nullValue());
|
||||
assertThat("Client Close Event Reason",closeReason,nullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertThat("Client Close Event Reason",closeReason,reasonMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public void assertReceivedError(Class<? extends Throwable> expectedThrownClass, Matcher<String> messageMatcher) throws TimeoutException,
|
||||
InterruptedException
|
||||
{
|
||||
errorQueue.awaitEventCount(1,30,TimeUnit.SECONDS);
|
||||
Throwable actual = errorQueue.poll();
|
||||
Assert.assertThat("Client Error Event",actual,instanceOf(expectedThrownClass));
|
||||
if (messageMatcher == null)
|
||||
{
|
||||
Assert.assertThat("Client Error Event Message",actual.getMessage(),nullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertThat("Client Error Event Message",actual.getMessage(),messageMatcher);
|
||||
assertThat("Client Close Event Reason",closeReason,reasonMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearQueues()
|
||||
{
|
||||
messageQueue.clear();
|
||||
errorQueue.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -164,7 +148,8 @@ public class ClientCloseTest
|
|||
public void onWebSocketError(Throwable cause)
|
||||
{
|
||||
LOG.debug("onWebSocketError",cause);
|
||||
Assert.assertThat("Error capture",errorQueue.offer(cause),is(true));
|
||||
assertThat("Unique Error Event", error.compareAndSet(null, cause), is(true));
|
||||
errorLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,15 +162,15 @@ public class ClientCloseTest
|
|||
public EndPoint getEndPoint() throws Exception
|
||||
{
|
||||
Session session = getSession();
|
||||
Assert.assertThat("Session type",session,instanceOf(WebSocketSession.class));
|
||||
assertThat("Session type",session,instanceOf(WebSocketSession.class));
|
||||
|
||||
WebSocketSession wssession = (WebSocketSession)session;
|
||||
Field fld = wssession.getClass().getDeclaredField("connection");
|
||||
fld.setAccessible(true);
|
||||
Assert.assertThat("Field: connection",fld,notNullValue());
|
||||
assertThat("Field: connection",fld,notNullValue());
|
||||
|
||||
Object val = fld.get(wssession);
|
||||
Assert.assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
|
||||
assertThat("Connection type",val,instanceOf(AbstractWebSocketConnection.class));
|
||||
@SuppressWarnings("resource")
|
||||
AbstractWebSocketConnection wsconn = (AbstractWebSocketConnection)val;
|
||||
return wsconn.getEndPoint();
|
||||
|
@ -204,7 +189,7 @@ public class ClientCloseTest
|
|||
clientFuture.get(30,TimeUnit.SECONDS);
|
||||
|
||||
// Wait for client connect via client websocket
|
||||
Assert.assertThat("Client WebSocket is Open",clientSocket.openLatch.await(30,TimeUnit.SECONDS),is(true));
|
||||
assertThat("Client WebSocket is Open",clientSocket.openLatch.await(30,TimeUnit.SECONDS),is(true));
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -220,8 +205,8 @@ public class ClientCloseTest
|
|||
serverCapture.assertNoErrors();
|
||||
serverCapture.assertFrameCount(1);
|
||||
WebSocketFrame frame = serverCapture.getFrames().poll();
|
||||
Assert.assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
|
||||
Assert.assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
|
||||
assertThat("Server received frame",frame.getOpCode(),is(OpCode.TEXT));
|
||||
assertThat("Server received frame payload",frame.getPayloadAsUTF8(),is(echoMsg));
|
||||
|
||||
// Server send echo reply
|
||||
serverConns.write(new TextFrame().setPayload(echoMsg));
|
||||
|
@ -231,10 +216,10 @@ public class ClientCloseTest
|
|||
|
||||
// Verify received message
|
||||
String recvMsg = clientSocket.messageQueue.poll();
|
||||
Assert.assertThat("Received message",recvMsg,is(echoMsg));
|
||||
assertThat("Received message",recvMsg,is(echoMsg));
|
||||
|
||||
// Verify that there are no errors
|
||||
Assert.assertThat("Error events",clientSocket.errorQueue,empty());
|
||||
assertThat("Error events",clientSocket.error.get(),nullValue());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -250,16 +235,16 @@ public class ClientCloseTest
|
|||
serverCapture.assertFrameCount(1);
|
||||
serverCapture.assertHasFrame(OpCode.CLOSE,1);
|
||||
WebSocketFrame frame = serverCapture.getFrames().poll();
|
||||
Assert.assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
assertThat("Server received close frame",frame.getOpCode(),is(OpCode.CLOSE));
|
||||
CloseInfo closeInfo = new CloseInfo(frame);
|
||||
Assert.assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
|
||||
assertThat("Server received close code",closeInfo.getStatusCode(),is(expectedCloseCode));
|
||||
if (closeReasonMatcher == null)
|
||||
{
|
||||
Assert.assertThat("Server received close reason",closeInfo.getReason(),nullValue());
|
||||
assertThat("Server received close reason",closeInfo.getReason(),nullValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
|
||||
assertThat("Server received close reason",closeInfo.getReason(),closeReasonMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,12 +349,12 @@ public class ClientCloseTest
|
|||
|
||||
// Verify received messages
|
||||
String recvMsg = clientSocket.messageQueue.poll();
|
||||
Assert.assertThat("Received message 1",recvMsg,is("Hello"));
|
||||
assertThat("Received message 1",recvMsg,is("Hello"));
|
||||
recvMsg = clientSocket.messageQueue.poll();
|
||||
Assert.assertThat("Received message 2",recvMsg,is("World"));
|
||||
assertThat("Received message 2",recvMsg,is("World"));
|
||||
|
||||
// Verify that there are no errors
|
||||
Assert.assertThat("Error events",clientSocket.errorQueue,empty());
|
||||
assertThat("Error events",clientSocket.error.get(),nullValue());
|
||||
|
||||
// client close event on ws-endpoint
|
||||
clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.NORMAL),containsString("From Server"));
|
||||
|
@ -399,7 +384,7 @@ public class ClientCloseTest
|
|||
// when write is congested, client enqueue close frame
|
||||
// client initiate write, but write never completes
|
||||
EndPoint endp = clientSocket.getEndPoint();
|
||||
Assert.assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
|
||||
assertThat("EndPoint is testable",endp,instanceOf(TestEndPoint.class));
|
||||
TestEndPoint testendp = (TestEndPoint)endp;
|
||||
|
||||
char msg[] = new char[10240];
|
||||
|
@ -418,7 +403,7 @@ public class ClientCloseTest
|
|||
LOG.debug("Wrote {} frames totalling {} bytes of payload before congestion kicked in",writeCount,writeSize);
|
||||
|
||||
// Verify that there are no errors
|
||||
Assert.assertThat("Error events",clientSocket.errorQueue,empty());
|
||||
assertThat("Error events",clientSocket.error.get(),nullValue());
|
||||
|
||||
// client idle timeout triggers close event on client ws-endpoint
|
||||
// client close event on ws-endpoint
|
||||
|
@ -462,7 +447,9 @@ public class ClientCloseTest
|
|||
serverConn.write(bad);
|
||||
|
||||
// client should have noticed the error
|
||||
clientSocket.assertReceivedError(ProtocolException.class,containsString("Invalid control frame"));
|
||||
assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat("OnError", clientSocket.error.get(), instanceOf(ProtocolException.class));
|
||||
assertThat("OnError", clientSocket.error.get().getMessage(), containsString("Invalid control frame"));
|
||||
|
||||
// client parse invalid frame, notifies server of close (protocol error)
|
||||
confirmServerReceivedCloseFrame(serverConn,StatusCode.PROTOCOL,allOf(containsString("Invalid control frame"),containsString("length")));
|
||||
|
@ -512,8 +499,6 @@ public class ClientCloseTest
|
|||
}
|
||||
|
||||
@Test
|
||||
// TODO work out why this test is failing
|
||||
@Ignore
|
||||
public void testServerNoCloseHandshake() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
|
@ -545,7 +530,9 @@ public class ClientCloseTest
|
|||
// server sits idle
|
||||
|
||||
// client idle timeout triggers close event on client ws-endpoint
|
||||
clientSocket.assertReceivedCloseEvent(timeout,is(StatusCode.SHUTDOWN),containsString("Timeout"));
|
||||
assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat("OnError", clientSocket.error.get(), instanceOf(SocketTimeoutException.class));
|
||||
assertThat("OnError", clientSocket.error.get().getMessage(), containsString("Timeout on Read"));
|
||||
}
|
||||
|
||||
@Test(timeout = 5000L)
|
||||
|
@ -617,8 +604,9 @@ public class ClientCloseTest
|
|||
// client write failure
|
||||
final String origCloseReason = "Normal Close";
|
||||
clientSocket.getSession().close(StatusCode.NORMAL,origCloseReason);
|
||||
|
||||
clientSocket.assertReceivedError(EofException.class,null);
|
||||
|
||||
assertThat("OnError Latch", clientSocket.errorLatch.await(2, TimeUnit.SECONDS), is(true));
|
||||
assertThat("OnError", clientSocket.error.get(), instanceOf(EofException.class));
|
||||
|
||||
// client triggers close event on client ws-endpoint
|
||||
// assert - close code==1006 (abnormal)
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.websocket.common.io;
|
||||
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
|
||||
/**
|
||||
* Wraps the exposed {@link WriteCallback} WebSocket API with a Jetty {@link Callback}.
|
||||
* <p>
|
||||
* We don't expose the jetty {@link Callback} object to the webapp, as that makes things complicated for the WebAppContext's Classloader.
|
||||
*/
|
||||
public class WriteCallbackWrapper implements Callback
|
||||
{
|
||||
public static Callback wrap(WriteCallback callback)
|
||||
{
|
||||
if (callback == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new WriteCallbackWrapper(callback);
|
||||
}
|
||||
|
||||
private final WriteCallback callback;
|
||||
|
||||
public WriteCallbackWrapper(WriteCallback callback)
|
||||
{
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
callback.writeFailed(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
callback.writeSuccess();
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.websocket.common.io;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.websocket.api.WriteCallback;
|
||||
|
||||
/**
|
||||
* Tracking Callback for testing how the callbacks are used.
|
||||
*/
|
||||
public class TrackingCallback implements Callback, WriteCallback
|
||||
{
|
||||
private AtomicInteger called = new AtomicInteger();
|
||||
private boolean success = false;
|
||||
private Throwable failure = null;
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
this.called.incrementAndGet();
|
||||
this.success = false;
|
||||
this.failure = x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
this.called.incrementAndGet();
|
||||
this.success = false;
|
||||
}
|
||||
|
||||
public Throwable getFailure()
|
||||
{
|
||||
return failure;
|
||||
}
|
||||
|
||||
public boolean isSuccess()
|
||||
{
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean isCalled()
|
||||
{
|
||||
return called.get() >= 1;
|
||||
}
|
||||
|
||||
public int getCallCount()
|
||||
{
|
||||
return called.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFailed(Throwable x)
|
||||
{
|
||||
failed(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSuccess()
|
||||
{
|
||||
succeeded();
|
||||
}
|
||||
}
|
|
@ -146,9 +146,9 @@ public class HttpChannelAssociationTest extends AbstractTest
|
|||
return new HttpConnectionOverHTTP2(destination, session)
|
||||
{
|
||||
@Override
|
||||
protected HttpChannelOverHTTP2 newHttpChannel()
|
||||
protected HttpChannelOverHTTP2 newHttpChannel(boolean push)
|
||||
{
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession())
|
||||
return new HttpChannelOverHTTP2(getHttpDestination(), this, getSession(), push)
|
||||
{
|
||||
@Override
|
||||
public boolean associate(HttpExchange exchange)
|
||||
|
|
Loading…
Reference in New Issue