Merge branch 'master' into session-refactor
This commit is contained in:
commit
4793be634f
20
VERSION.txt
20
VERSION.txt
|
@ -31,6 +31,26 @@ jetty-9.3.6.v20151106 - 06 November 2015
|
|||
+ 481437 Port ConnectHandler connect and context functionality from Jetty 8.
|
||||
+ 481554 DispatcherType reset race
|
||||
|
||||
jetty-9.2.14.v20151106 - 06 November 2015
|
||||
+ 428474 Expose batch mode in the Jetty WebSocket API
|
||||
+ 471055 Restore legacy/experimental WebSocket extensions (deflate-frame)
|
||||
+ 472082 isOpen returns true on CLOSING Connection
|
||||
+ 474068 Update WebSocket Extension for permessage-deflate draft-22
|
||||
+ 474319 Reintroduce blocking connect().
|
||||
+ 474321 Allow synchronous address resolution.
|
||||
+ 474453 Tiny buffers (under 7 bytes) fail to compress in permessage-deflate
|
||||
+ 474454 Backport permessage-deflate from Jetty 9.3.x to 9.2.x
|
||||
+ 474936 WebSocketSessions are not always cleaned out from openSessions
|
||||
+ 476023 Incorrect trimming of WebSocket close reason
|
||||
+ 476049 When using WebSocket Session.close() there should be no status code
|
||||
or reason sent
|
||||
+ 477385 Problem in MANIFEST.MF with version 9.2.10 / 9.2.13.
|
||||
+ 477817 Fixed memory leak in QueuedThreadPool
|
||||
+ 481006 SSL requests intermittently fail with EOFException when SSL
|
||||
renegotiation is disallowed.
|
||||
+ 481236 Make ShutdownMonitor java security manager friendly
|
||||
+ 481437 Port ConnectHandler connect and context functionality from Jetty 8.
|
||||
|
||||
jetty-9.3.5.v20151012 - 12 October 2015
|
||||
+ 479343 calls to MetaData#orderFragments() with relative ordering adds
|
||||
duplicate jars
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.annotation.WebInitParam;
|
||||
|
@ -28,6 +30,7 @@ import javax.servlet.http.HttpServlet;
|
|||
import org.eclipse.jetty.servlet.Holder;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.ServletMapping;
|
||||
import org.eclipse.jetty.util.ArrayUtil;
|
||||
import org.eclipse.jetty.util.LazyList;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -50,13 +53,13 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
{
|
||||
super(context, className);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public WebServletAnnotation (WebAppContext context, String className, Resource resource)
|
||||
{
|
||||
super(context, className, resource);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DiscoveredAnnotation#apply()
|
||||
*/
|
||||
|
@ -104,10 +107,11 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
String servletName = (annotation.name().equals("")?clazz.getName():annotation.name());
|
||||
|
||||
MetaData metaData = _context.getMetaData();
|
||||
ServletMapping mapping = null; //the new mapping
|
||||
|
||||
//Find out if a <servlet> already exists with this name
|
||||
ServletHolder[] holders = _context.getServletHandler().getServlets();
|
||||
boolean isNew = true;
|
||||
|
||||
ServletHolder holder = null;
|
||||
if (holders != null)
|
||||
{
|
||||
|
@ -116,13 +120,13 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
if (h.getName() != null && servletName.equals(h.getName()))
|
||||
{
|
||||
holder = h;
|
||||
isNew = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isNew)
|
||||
//handle creation/completion of a servlet
|
||||
if (holder == null)
|
||||
{
|
||||
//No servlet of this name has already been defined, either by a descriptor
|
||||
//or another annotation (which would be impossible).
|
||||
|
@ -147,11 +151,11 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
}
|
||||
|
||||
_context.getServletHandler().addServlet(holder);
|
||||
ServletMapping mapping = new ServletMapping();
|
||||
|
||||
|
||||
mapping = new ServletMapping();
|
||||
mapping.setServletName(holder.getName());
|
||||
mapping.setPathSpecs( LazyList.toStringArray(urlPatternList));
|
||||
_context.getServletHandler().addServletMapping(mapping);
|
||||
metaData.setOrigin(servletName+".servlet.mappings",annotation,clazz);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -175,54 +179,102 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//check the url-patterns
|
||||
//ServletSpec 3.0 p81 If a servlet already has url mappings from a
|
||||
//webxml or fragment descriptor the annotation is ignored. However, we want to be able to
|
||||
//replace mappings that were given in webdefault.xml
|
||||
boolean mappingsExist = false;
|
||||
boolean anyNonDefaults = false;
|
||||
ServletMapping[] allMappings = _context.getServletHandler().getServletMappings();
|
||||
if (allMappings != null)
|
||||
{
|
||||
for (ServletMapping m:allMappings)
|
||||
{
|
||||
if (m.getServletName() != null && servletName.equals(m.getServletName()))
|
||||
{
|
||||
mappingsExist = true;
|
||||
if (!m.isDefault())
|
||||
{
|
||||
anyNonDefaults = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//webxml or fragment descriptor the annotation is ignored.
|
||||
//However, we want to be able to replace mappings that were given in webdefault.xml
|
||||
List<ServletMapping> existingMappings = getServletMappingsForServlet(servletName);
|
||||
|
||||
if (anyNonDefaults)
|
||||
return; //if any mappings already set by a descriptor that is not webdefault.xml, we're done
|
||||
|
||||
boolean clash = false;
|
||||
if (mappingsExist)
|
||||
//if any mappings for this servlet already set by a descriptor that is not webdefault.xml forget
|
||||
//about processing these url mappings
|
||||
if (existingMappings.isEmpty() || !containsNonDefaultMappings(existingMappings))
|
||||
{
|
||||
for (String p:urlPatternList)
|
||||
{
|
||||
ServletMapping m = _context.getServletHandler().getServletMapping(p);
|
||||
if (m != null && !m.isDefault())
|
||||
{
|
||||
//trying to override a servlet-mapping that was added not by webdefault.xml
|
||||
clash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mappingsExist || !clash)
|
||||
{
|
||||
ServletMapping m = new ServletMapping();
|
||||
m.setServletName(servletName);
|
||||
m.setPathSpecs(LazyList.toStringArray(urlPatternList));
|
||||
_context.getServletHandler().addServletMapping(m);
|
||||
mapping = new ServletMapping();
|
||||
mapping.setServletName(servletName);
|
||||
mapping.setPathSpecs(LazyList.toStringArray(urlPatternList));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//We also want to be able to replace mappings that were defined in webdefault.xml
|
||||
//that were for a different servlet eg a mapping in webdefault.xml for / to the jetty
|
||||
//default servlet should be able to be replaced by an annotation for / to a different
|
||||
//servlet
|
||||
if (mapping != null)
|
||||
{
|
||||
//url mapping was permitted by annotation processing rules
|
||||
|
||||
//take a copy of the existing servlet mappings that we can iterate over and remove from. This is
|
||||
//because the ServletHandler interface does not support removal of individual mappings.
|
||||
List<ServletMapping> allMappings = ArrayUtil.asMutableList(_context.getServletHandler().getServletMappings());
|
||||
|
||||
//for each of the urls in the annotation, check if a mapping to same/different servlet exists
|
||||
// if mapping exists and is from a default descriptor, it can be replaced. NOTE: we do not
|
||||
// guard against duplicate path mapping here: that is the job of the ServletHandler
|
||||
for (String p:urlPatternList)
|
||||
{
|
||||
ServletMapping existingMapping = _context.getServletHandler().getServletMapping(p);
|
||||
if (existingMapping != null && existingMapping.isDefault())
|
||||
{
|
||||
String[] updatedPaths = ArrayUtil.removeFromArray(existingMapping.getPathSpecs(), p);
|
||||
//if we removed the last path from a servletmapping, delete the servletmapping
|
||||
if (updatedPaths == null || updatedPaths.length == 0)
|
||||
{
|
||||
boolean success = allMappings.remove(existingMapping);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Removed empty mapping {} from defaults descriptor success:{}",existingMapping, success);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingMapping.setPathSpecs(updatedPaths);
|
||||
if (LOG.isDebugEnabled()) LOG.debug("Removed path {} from mapping {} from defaults descriptor ", p,existingMapping);
|
||||
}
|
||||
}
|
||||
_context.getMetaData().setOrigin(servletName+".servlet.mapping."+p, annotation, clazz);
|
||||
}
|
||||
allMappings.add(mapping);
|
||||
_context.getServletHandler().setServletMappings(allMappings.toArray(new ServletMapping[allMappings.size()]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private List<ServletMapping> getServletMappingsForServlet (String name)
|
||||
{
|
||||
ServletMapping[] allMappings = _context.getServletHandler().getServletMappings();
|
||||
if (allMappings == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<ServletMapping> mappings = new ArrayList<ServletMapping>();
|
||||
for (ServletMapping m:allMappings)
|
||||
{
|
||||
if (m.getServletName() != null && name.equals(m.getServletName()))
|
||||
{
|
||||
mappings.add(m);
|
||||
}
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mappings
|
||||
* @return
|
||||
*/
|
||||
private boolean containsNonDefaultMappings (List<ServletMapping> mappings)
|
||||
{
|
||||
if (mappings == null)
|
||||
return false;
|
||||
for (ServletMapping m:mappings)
|
||||
{
|
||||
if (!m.isDefault())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.annotations;
|
||||
|
||||
import javax.servlet.annotation.WebInitParam;
|
||||
import javax.servlet.annotation.WebServlet;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
|
||||
|
||||
@WebServlet(urlPatterns = { "/", "/bah/*" }, name="DServlet", initParams={@WebInitParam(name="x", value="y")}, loadOnStartup=1, asyncSupported=false)
|
||||
public class ServletD extends HttpServlet
|
||||
{
|
||||
|
||||
}
|
|
@ -18,12 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.annotations;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -37,6 +31,13 @@ import org.eclipse.jetty.webapp.DiscoveredAnnotation;
|
|||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* TestServletAnnotations
|
||||
*/
|
||||
|
@ -59,7 +60,7 @@ public class TestServletAnnotations
|
|||
_list.add(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testServletAnnotation() throws Exception
|
||||
{
|
||||
|
@ -69,9 +70,9 @@ public class TestServletAnnotations
|
|||
|
||||
WebAppContext wac = new WebAppContext();
|
||||
List<DiscoveredAnnotation> results = new ArrayList<DiscoveredAnnotation>();
|
||||
|
||||
|
||||
TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results);
|
||||
|
||||
|
||||
parser.parse(Collections.singleton(handler), classes, new ClassNameResolver ()
|
||||
{
|
||||
public boolean isExcluded(String name)
|
||||
|
@ -85,7 +86,7 @@ public class TestServletAnnotations
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertTrue(results.get(0) instanceof WebServletAnnotation);
|
||||
|
||||
|
@ -94,14 +95,14 @@ public class TestServletAnnotations
|
|||
ServletHolder[] holders = wac.getServletHandler().getServlets();
|
||||
assertNotNull(holders);
|
||||
assertEquals(1, holders.length);
|
||||
|
||||
|
||||
// Verify servlet annotations
|
||||
ServletHolder cholder = holders[0];
|
||||
assertThat("Servlet Name", cholder.getName(), is("CServlet"));
|
||||
assertThat("InitParameter[x]", cholder.getInitParameter("x"), is("y"));
|
||||
assertThat("Init Order", cholder.getInitOrder(), is(2));
|
||||
assertThat("Async Supported", cholder.isAsyncSupported(), is(false));
|
||||
|
||||
|
||||
// Verify mappings
|
||||
ServletMapping[] mappings = wac.getServletHandler().getServletMappings();
|
||||
assertNotNull(mappings);
|
||||
|
@ -111,6 +112,195 @@ public class TestServletAnnotations
|
|||
assertEquals(2, paths.length);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWebServletAnnotationOverrideDefault () throws Exception
|
||||
{
|
||||
//if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
|
||||
//DO allow the annotation to replace the mapping.
|
||||
|
||||
WebAppContext wac = new WebAppContext();
|
||||
ServletHolder defaultServlet = new ServletHolder();
|
||||
defaultServlet.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
|
||||
defaultServlet.setName("default");
|
||||
wac.getServletHandler().addServlet(defaultServlet);
|
||||
|
||||
ServletMapping m = new ServletMapping();
|
||||
m.setPathSpec("/");
|
||||
m.setServletName("default");
|
||||
m.setDefault(true); //this mapping will be from a default descriptor
|
||||
wac.getServletHandler().addServletMapping(m);
|
||||
|
||||
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
|
||||
annotation.apply();
|
||||
|
||||
//test that as the original servlet mapping had only 1 pathspec, then the whole
|
||||
//servlet mapping should be deleted as that pathspec will be remapped to the DServlet
|
||||
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
|
||||
assertNotNull(resultMappings);
|
||||
assertEquals(1, resultMappings.length);
|
||||
assertEquals(2, resultMappings[0].getPathSpecs().length);
|
||||
resultMappings[0].getServletName().equals("DServlet");
|
||||
for (String s:resultMappings[0].getPathSpecs())
|
||||
{
|
||||
assertTrue (s.equals("/") || s.equals("/bah/*"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testWebServletAnnotationReplaceDefault () throws Exception
|
||||
{
|
||||
//if the existing servlet mapping TO A DIFFERENT SERVLET IS from a default descriptor we
|
||||
//DO allow the annotation to replace the mapping.
|
||||
WebAppContext wac = new WebAppContext();
|
||||
ServletHolder defaultServlet = new ServletHolder();
|
||||
defaultServlet.setClassName("org.eclipse.jetty.servlet.DefaultServlet");
|
||||
defaultServlet.setName("default");
|
||||
wac.getServletHandler().addServlet(defaultServlet);
|
||||
|
||||
ServletMapping m = new ServletMapping();
|
||||
m.setPathSpec("/");
|
||||
m.setServletName("default");
|
||||
m.setDefault(true); //this mapping will be from a default descriptor
|
||||
wac.getServletHandler().addServletMapping(m);
|
||||
|
||||
ServletMapping m2 = new ServletMapping();
|
||||
m2.setPathSpec("/other");
|
||||
m2.setServletName("default");
|
||||
m2.setDefault(true); //this mapping will be from a default descriptor
|
||||
wac.getServletHandler().addServletMapping(m2);
|
||||
|
||||
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
|
||||
annotation.apply();
|
||||
|
||||
//test that only the mapping for "/" was removed from the mappings to the default servlet
|
||||
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
|
||||
assertNotNull(resultMappings);
|
||||
assertEquals(2, resultMappings.length);
|
||||
for (ServletMapping r:resultMappings)
|
||||
{
|
||||
if (r.getServletName().equals("default"))
|
||||
{
|
||||
assertEquals(1,r.getPathSpecs().length);
|
||||
assertEquals("/other", r.getPathSpecs()[0]);
|
||||
}
|
||||
else if (r.getServletName().equals("DServlet"))
|
||||
{
|
||||
assertEquals(2,r.getPathSpecs().length);
|
||||
for (String p:r.getPathSpecs())
|
||||
{
|
||||
if (!p.equals("/") && !p.equals("/bah/*"))
|
||||
fail("Unexpected path");
|
||||
}
|
||||
}
|
||||
else
|
||||
fail("Unexpected servlet mapping");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testWebServletAnnotationNotOverride () throws Exception
|
||||
{
|
||||
//if the existing servlet mapping TO A DIFFERENT SERVLET IS NOT from a default descriptor we
|
||||
//DO NOT allow the annotation to replace the mapping
|
||||
WebAppContext wac = new WebAppContext();
|
||||
ServletHolder servlet = new ServletHolder();
|
||||
servlet.setClassName("org.eclipse.jetty.servlet.FooServlet");
|
||||
servlet.setName("foo");
|
||||
wac.getServletHandler().addServlet(servlet);
|
||||
ServletMapping m = new ServletMapping();
|
||||
m.setPathSpec("/");
|
||||
m.setServletName("foo");
|
||||
wac.getServletHandler().addServletMapping(m);
|
||||
|
||||
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
|
||||
annotation.apply();
|
||||
|
||||
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
|
||||
assertEquals(2, resultMappings.length);
|
||||
for (ServletMapping r:resultMappings)
|
||||
{
|
||||
if (r.getServletName().equals("DServlet"))
|
||||
{
|
||||
assertEquals(2, r.getPathSpecs().length);
|
||||
}
|
||||
else if (r.getServletName().equals("foo"))
|
||||
{
|
||||
assertEquals(1, r.getPathSpecs().length);
|
||||
}
|
||||
else
|
||||
fail("Unexpected servlet name");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebServletAnnotationIgnore () throws Exception
|
||||
{
|
||||
//an existing servlet OF THE SAME NAME has even 1 non-default mapping we can't use
|
||||
//any of the url mappings in the annotation
|
||||
WebAppContext wac = new WebAppContext();
|
||||
ServletHolder servlet = new ServletHolder();
|
||||
servlet.setClassName("org.eclipse.jetty.servlet.OtherDServlet");
|
||||
servlet.setName("DServlet");
|
||||
wac.getServletHandler().addServlet(servlet);
|
||||
|
||||
ServletMapping m = new ServletMapping();
|
||||
m.setPathSpec("/default");
|
||||
m.setDefault(true);
|
||||
m.setServletName("DServlet");
|
||||
wac.getServletHandler().addServletMapping(m);
|
||||
|
||||
ServletMapping m2 = new ServletMapping();
|
||||
m2.setPathSpec("/other");
|
||||
m2.setServletName("DServlet");
|
||||
wac.getServletHandler().addServletMapping(m2);
|
||||
|
||||
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
|
||||
annotation.apply();
|
||||
|
||||
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
|
||||
assertEquals(2, resultMappings.length);
|
||||
|
||||
for (ServletMapping r:resultMappings)
|
||||
{
|
||||
assertEquals(1, r.getPathSpecs().length);
|
||||
if (!r.getPathSpecs()[0].equals("/default") && !r.getPathSpecs()[0].equals("/other"))
|
||||
fail("Unexpected path in mapping");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebServletAnnotationNoMappings () throws Exception
|
||||
{
|
||||
//an existing servlet OF THE SAME NAME has no mappings, therefore all mappings in the annotation
|
||||
//should be accepted
|
||||
WebAppContext wac = new WebAppContext();
|
||||
ServletHolder servlet = new ServletHolder();
|
||||
servlet.setName("foo");
|
||||
wac.getServletHandler().addServlet(servlet);
|
||||
|
||||
|
||||
WebServletAnnotation annotation = new WebServletAnnotation(wac, "org.eclipse.jetty.annotations.ServletD", null);
|
||||
annotation.apply();
|
||||
|
||||
ServletMapping[] resultMappings = wac.getServletHandler().getServletMappings();
|
||||
assertEquals(1, resultMappings.length);
|
||||
assertEquals(2, resultMappings[0].getPathSpecs().length);
|
||||
for (String s:resultMappings[0].getPathSpecs())
|
||||
{
|
||||
if (!s.equals("/") && !s.equals("/bah/*"))
|
||||
fail("Unexpected path mapping");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testDeclareRoles ()
|
||||
throws Exception
|
||||
{
|
||||
|
|
|
@ -324,27 +324,34 @@ public abstract class HttpReceiver
|
|||
}
|
||||
else
|
||||
{
|
||||
List<ByteBuffer> decodeds = new ArrayList<>(2);
|
||||
while (buffer.hasRemaining())
|
||||
try
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
if (!decoded.hasRemaining())
|
||||
continue;
|
||||
decodeds.add(decoded);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
}
|
||||
List<ByteBuffer> decodeds = new ArrayList<>(2);
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
ByteBuffer decoded = decoder.decode(buffer);
|
||||
if (!decoded.hasRemaining())
|
||||
continue;
|
||||
decodeds.add(decoded);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
|
||||
}
|
||||
|
||||
if (decodeds.isEmpty())
|
||||
{
|
||||
callback.succeeded();
|
||||
if (decodeds.isEmpty())
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = decodeds.size();
|
||||
CountingCallback counter = new CountingCallback(callback, size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
notifier.notifyContent(listeners, response, decodeds.get(i), counter);
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Throwable x)
|
||||
{
|
||||
int size = decodeds.size();
|
||||
CountingCallback counter = new CountingCallback(callback, size);
|
||||
for (int i = 0; i < size; ++i)
|
||||
notifier.notifyContent(listeners, response, decodeds.get(i), counter);
|
||||
callback.failed(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -218,16 +218,17 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method));
|
||||
exchange.getResponse().version(version).status(status).reason(reason);
|
||||
|
||||
responseBegin(exchange);
|
||||
return false;
|
||||
return !responseBegin(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedHeader(HttpField field)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange != null)
|
||||
responseHeader(exchange, field);
|
||||
if (exchange == null)
|
||||
return;
|
||||
|
||||
responseHeader(exchange, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -237,8 +238,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
if (exchange == null)
|
||||
return false;
|
||||
|
||||
responseHeaders(exchange);
|
||||
return false;
|
||||
return !responseHeaders(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -263,17 +263,20 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
failAndClose(x);
|
||||
}
|
||||
};
|
||||
responseContent(exchange, buffer, callback);
|
||||
return callback.tryComplete();
|
||||
// Do not short circuit these calls.
|
||||
boolean proceed = responseContent(exchange, buffer, callback);
|
||||
boolean async = callback.tryComplete();
|
||||
return !proceed || async;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean messageComplete()
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange != null)
|
||||
responseSuccess(exchange);
|
||||
return false;
|
||||
if (exchange == null)
|
||||
return false;
|
||||
|
||||
return !responseSuccess(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.io.ByteArrayOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
|
@ -196,6 +197,33 @@ public class HttpClientGZIPTest extends AbstractHttpClientServerTest
|
|||
Assert.assertArrayEquals(data, response.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGZIPContentCorrupted() throws Exception
|
||||
{
|
||||
start(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setHeader("Content-Encoding", "gzip");
|
||||
// Not gzipped, will cause the client to blow up.
|
||||
response.getOutputStream().print("0123456789");
|
||||
}
|
||||
});
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("localhost", connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
latch.countDown();
|
||||
});
|
||||
|
||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private static void sleep(long ms) throws IOException
|
||||
{
|
||||
try
|
||||
|
|
|
@ -374,9 +374,10 @@ public class HttpConnectionOverFCGI extends AbstractConnection implements Connec
|
|||
close(x);
|
||||
}
|
||||
};
|
||||
if (!channel.content(buffer, callback))
|
||||
return true;
|
||||
return callback.tryComplete();
|
||||
// Do not short circuit these calls.
|
||||
boolean proceed = channel.content(buffer, callback);
|
||||
boolean async = callback.tryComplete();
|
||||
return !proceed || async;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -75,7 +75,12 @@ public class HeaderBlockParser
|
|||
MetaData result = hpackDecoder.decode(toDecode);
|
||||
|
||||
buffer.limit(limit);
|
||||
byteBufferPool.release(blockBuffer);
|
||||
|
||||
if (blockBuffer != null)
|
||||
{
|
||||
byteBufferPool.release(blockBuffer);
|
||||
blockBuffer = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -130,18 +130,23 @@ public class DataGenerateParseTest
|
|||
}
|
||||
}, 4096, 8192);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
ByteBuffer data = ByteBuffer.wrap(largeContent);
|
||||
generator.generateData(lease, 13, data.slice(), true, data.remaining());
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
ByteBuffer data = ByteBuffer.wrap(largeContent);
|
||||
generator.generateData(lease, 13, data.slice(), true, data.remaining());
|
||||
|
||||
Assert.assertEquals(largeContent.length, frames.size());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(largeContent.length, frames.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,21 +96,26 @@ public class GoAwayGenerateParseTest
|
|||
byte[] payload = new byte[16];
|
||||
new Random().nextBytes(payload);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateGoAway(lease, lastStreamId, error, payload);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateGoAway(lease, lastStreamId, error, payload);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
GoAwayFrame frame = frames.get(0);
|
||||
Assert.assertEquals(lastStreamId, frame.getLastStreamId());
|
||||
Assert.assertEquals(error, frame.getError());
|
||||
Assert.assertArrayEquals(payload, frame.getPayload());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
GoAwayFrame frame = frames.get(0);
|
||||
Assert.assertEquals(lastStreamId, frame.getLastStreamId());
|
||||
Assert.assertEquals(error, frame.getError());
|
||||
Assert.assertArrayEquals(payload, frame.getPayload());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,41 +114,47 @@ public class HeadersGenerateParseTest
|
|||
}
|
||||
}, 4096, 8192);
|
||||
|
||||
int streamId = 13;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true);
|
||||
generator.generateHeaders(lease, streamId, metaData, priorityFrame, true);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
int streamId = 13;
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.put("Accept", "text/html");
|
||||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
PriorityFrame priorityFrame = new PriorityFrame(streamId, 3 * streamId, 200, true);
|
||||
generator.generateHeaders(lease, streamId, metaData, priorityFrame, true);
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
buffer = buffer.slice();
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
HeadersFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertTrue(frame.isEndStream());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getURI(), request.getURI());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
Assert.assertEquals(1, frames.size());
|
||||
HeadersFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertTrue(frame.isEndStream());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getURI(), request.getURI());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
PriorityFrame priority = frame.getPriority();
|
||||
Assert.assertNotNull(priority);
|
||||
Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
|
||||
Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
|
||||
Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
|
||||
Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
|
||||
}
|
||||
PriorityFrame priority = frame.getPriority();
|
||||
Assert.assertNotNull(priority);
|
||||
Assert.assertEquals(priorityFrame.getStreamId(), priority.getStreamId());
|
||||
Assert.assertEquals(priorityFrame.getParentStreamId(), priority.getParentStreamId());
|
||||
Assert.assertEquals(priorityFrame.getWeight(), priority.getWeight());
|
||||
Assert.assertEquals(priorityFrame.isExclusive(), priority.isExclusive());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,21 +93,26 @@ public class PingGenerateParseTest
|
|||
byte[] payload = new byte[8];
|
||||
new Random().nextBytes(payload);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePing(lease, payload, true);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePing(lease, payload, true);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PingFrame frame = frames.get(0);
|
||||
Assert.assertArrayEquals(payload, frame.getPayload());
|
||||
Assert.assertTrue(frame.isReply());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PingFrame frame = frames.get(0);
|
||||
Assert.assertArrayEquals(payload, frame.getPayload());
|
||||
Assert.assertTrue(frame.isReply());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -98,22 +98,27 @@ public class PriorityGenerateParseTest
|
|||
int weight = 3;
|
||||
boolean exclusive = true;
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePriority(lease, streamId, parentStreamId, weight, exclusive);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PriorityFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(parentStreamId, frame.getParentStreamId());
|
||||
Assert.assertEquals(weight, frame.getWeight());
|
||||
Assert.assertEquals(exclusive, frame.isExclusive());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PriorityFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(parentStreamId, frame.getParentStreamId());
|
||||
Assert.assertEquals(weight, frame.getWeight());
|
||||
Assert.assertEquals(exclusive, frame.isExclusive());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -115,28 +115,33 @@ public class PushPromiseGenerateParseTest
|
|||
fields.put("User-Agent", "Jetty");
|
||||
MetaData.Request metaData = new MetaData.Request("GET", HttpScheme.HTTP, new HostPortHttpField("localhost:8080"), "/path", HttpVersion.HTTP_2, fields);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generatePushPromise(lease, streamId, promisedStreamId, metaData);
|
||||
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PushPromiseFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getURI(), request.getURI());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
Assert.assertEquals(1, frames.size());
|
||||
PushPromiseFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(promisedStreamId, frame.getPromisedStreamId());
|
||||
MetaData.Request request = (MetaData.Request)frame.getMetaData();
|
||||
Assert.assertEquals(metaData.getMethod(), request.getMethod());
|
||||
Assert.assertEquals(metaData.getURI(), request.getURI());
|
||||
for (int j = 0; j < fields.size(); ++j)
|
||||
{
|
||||
HttpField field = fields.getField(j);
|
||||
Assert.assertTrue(request.getFields().contains(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,20 +92,25 @@ public class ResetGenerateParseTest
|
|||
int streamId = 13;
|
||||
int error = 17;
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateReset(lease, streamId, error);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateReset(lease, streamId, error);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
ResetFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(error, frame.getError());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
ResetFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(error, frame.getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -155,22 +155,27 @@ public class SettingsGenerateParseTest
|
|||
Integer value = 17;
|
||||
settings1.put(key, value);
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings1, true);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateSettings(lease, settings1, true);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
SettingsFrame frame = frames.get(0);
|
||||
Map<Integer, Integer> settings2 = frame.getSettings();
|
||||
Assert.assertEquals(1, settings2.size());
|
||||
Assert.assertEquals(value, settings2.get(key));
|
||||
Assert.assertTrue(frame.isReply());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
SettingsFrame frame = frames.get(0);
|
||||
Map<Integer, Integer> settings2 = frame.getSettings();
|
||||
Assert.assertEquals(1, settings2.size());
|
||||
Assert.assertEquals(value, settings2.get(key));
|
||||
Assert.assertTrue(frame.isReply());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,20 +92,25 @@ public class WindowUpdateGenerateParseTest
|
|||
int streamId = 13;
|
||||
int windowUpdate = 17;
|
||||
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateWindowUpdate(lease, streamId, windowUpdate);
|
||||
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
// Iterate a few times to be sure generator and parser are properly reset.
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
ByteBufferPool.Lease lease = new ByteBufferPool.Lease(byteBufferPool);
|
||||
generator.generateWindowUpdate(lease, streamId, windowUpdate);
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
WindowUpdateFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(windowUpdate, frame.getWindowDelta());
|
||||
frames.clear();
|
||||
for (ByteBuffer buffer : lease.getByteBuffers())
|
||||
{
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
parser.parse(ByteBuffer.wrap(new byte[]{buffer.get()}));
|
||||
}
|
||||
}
|
||||
|
||||
Assert.assertEquals(1, frames.size());
|
||||
WindowUpdateFrame frame = frames.get(0);
|
||||
Assert.assertEquals(streamId, frame.getStreamId());
|
||||
Assert.assertEquals(windowUpdate, frame.getWindowDelta());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -660,8 +660,6 @@ public class ServletHolder extends Holder<Servlet> implements UserIdentity.Scope
|
|||
/* Set the webapp's classpath for Jasper */
|
||||
ch.setAttribute("org.apache.catalina.jsp_classpath", ch.getClassPath());
|
||||
|
||||
System.err.println("JSP ("+ch+","+getName()+") CP="+ch.getClassPath());
|
||||
|
||||
/* Set up other classpath attribute */
|
||||
if ("?".equals(getInitParameter("classpath")))
|
||||
{
|
||||
|
|
|
@ -614,6 +614,9 @@ public class AsyncServletIOTest
|
|||
@Test
|
||||
public void testCompleteWhilePending() throws Exception
|
||||
{
|
||||
_servlet4.onDA.set(0);
|
||||
_servlet4.onWP.set(0);
|
||||
|
||||
StringBuilder request = new StringBuilder(512);
|
||||
request.append("POST /ctx/path4/info HTTP/1.1\r\n")
|
||||
.append("Host: localhost\r\n")
|
||||
|
@ -674,7 +677,7 @@ public class AsyncServletIOTest
|
|||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
||||
// ignored
|
||||
}
|
||||
|
||||
LOG.debug("last: "+last);
|
||||
|
@ -686,8 +689,8 @@ public class AsyncServletIOTest
|
|||
|
||||
assertTrue(_servlet4.completed.await(5, TimeUnit.SECONDS));
|
||||
Thread.sleep(100);
|
||||
assertEquals(2,_servlet4.onDA.get());
|
||||
assertEquals(2,_servlet4.onWP.get());
|
||||
assertEquals(0,_servlet4.onDA.get());
|
||||
assertEquals(0,_servlet4.onWP.get());
|
||||
|
||||
|
||||
}
|
||||
|
@ -708,7 +711,6 @@ public class AsyncServletIOTest
|
|||
|
||||
in.setReadListener(new ReadListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t)
|
||||
{
|
||||
|
@ -719,59 +721,61 @@ public class AsyncServletIOTest
|
|||
public void onDataAvailable() throws IOException
|
||||
{
|
||||
onDA.incrementAndGet();
|
||||
if (onDA.get()>2)
|
||||
return;
|
||||
|
||||
|
||||
boolean readF=false;
|
||||
// Read all available content
|
||||
while(in.isReady())
|
||||
if (in.read()<0)
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (onDA.get()==1)
|
||||
return;
|
||||
|
||||
final byte[] buffer = new byte[64*1024];
|
||||
Arrays.fill(buffer,(byte)'X');
|
||||
for (int i=199;i<buffer.length;i+=200)
|
||||
buffer[i]=(byte)'\n';
|
||||
|
||||
// Once we read block, let's make ourselves write blocked
|
||||
out.setWriteListener(new WriteListener()
|
||||
{
|
||||
@Override
|
||||
public void onWritePossible() throws IOException
|
||||
int c = in.read();
|
||||
if (c<0)
|
||||
throw new IllegalStateException();
|
||||
if (c=='F')
|
||||
readF=true;
|
||||
}
|
||||
|
||||
if (readF)
|
||||
{
|
||||
onDA.set(0);
|
||||
|
||||
final byte[] buffer = new byte[64*1024];
|
||||
Arrays.fill(buffer,(byte)'X');
|
||||
for (int i=199;i<buffer.length;i+=200)
|
||||
buffer[i]=(byte)'\n';
|
||||
|
||||
// Once we read block, let's make ourselves write blocked
|
||||
out.setWriteListener(new WriteListener()
|
||||
{
|
||||
onWP.incrementAndGet();
|
||||
if (onWP.get()>2)
|
||||
return;
|
||||
|
||||
while (out.isReady())
|
||||
out.write(buffer);
|
||||
|
||||
if (onWP.get()==1)
|
||||
return;
|
||||
|
||||
try
|
||||
@Override
|
||||
public void onWritePossible() throws IOException
|
||||
{
|
||||
// As soon as we are write blocked, complete
|
||||
async.complete();
|
||||
onWP.incrementAndGet();
|
||||
|
||||
while (out.isReady())
|
||||
out.write(buffer);
|
||||
|
||||
try
|
||||
{
|
||||
// As soon as we are write blocked, complete
|
||||
onWP.set(0);
|
||||
async.complete();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
completed.countDown();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t)
|
||||
{
|
||||
e.printStackTrace();
|
||||
t.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
completed.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -84,6 +84,10 @@ public abstract class CompletableCallback implements Callback
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FAILED:
|
||||
{
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException(current.toString());
|
||||
|
@ -110,6 +114,10 @@ public abstract class CompletableCallback implements Callback
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FAILED:
|
||||
{
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new IllegalStateException(current.toString());
|
||||
|
|
|
@ -20,7 +20,9 @@ package org.eclipse.jetty.websocket.jsr356.server;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.Extension.Parameter;
|
||||
|
@ -45,6 +47,7 @@ public class JsrCreator implements WebSocketCreator
|
|||
{
|
||||
public static final String PROP_REMOTE_ADDRESS = "javax.websocket.endpoint.remoteAddress";
|
||||
public static final String PROP_LOCAL_ADDRESS = "javax.websocket.endpoint.localAddress";
|
||||
public static final String PROP_LOCALES = "javax.websocket.upgrade.locales";
|
||||
private static final Logger LOG = Log.getLogger(JsrCreator.class);
|
||||
private final WebSocketContainerScope containerScope;
|
||||
private final ServerEndpointMetadata metadata;
|
||||
|
@ -74,8 +77,10 @@ public class JsrCreator implements WebSocketCreator
|
|||
// This is being implemented as an optional set of userProperties so that
|
||||
// it is not JSR api breaking. A few users on #jetty and a few from cometd
|
||||
// have asked for access to this information.
|
||||
config.getUserProperties().put(PROP_LOCAL_ADDRESS,req.getLocalSocketAddress());
|
||||
config.getUserProperties().put(PROP_REMOTE_ADDRESS,req.getRemoteSocketAddress());
|
||||
Map<String, Object> userProperties = config.getUserProperties();
|
||||
userProperties.put(PROP_LOCAL_ADDRESS,req.getLocalSocketAddress());
|
||||
userProperties.put(PROP_REMOTE_ADDRESS,req.getRemoteSocketAddress());
|
||||
userProperties.put(PROP_LOCALES,Collections.list(req.getLocales()));
|
||||
|
||||
// Get Configurator from config object (not guaranteed to be unique per endpoint upgrade)
|
||||
ServerEndpointConfig.Configurator configurator = config.getConfigurator();
|
||||
|
|
|
@ -28,13 +28,9 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.websocket.ClientEndpoint;
|
||||
import javax.websocket.CloseReason;
|
||||
|
@ -63,7 +59,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
|||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
|
||||
import org.eclipse.jetty.websocket.common.util.Hex;
|
||||
import org.eclipse.jetty.websocket.common.util.Sha1Sum;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
|
@ -176,33 +172,12 @@ public class StreamTest
|
|||
Assert.assertThat("Path should exist: " + file,file.exists(),is(true));
|
||||
Assert.assertThat("Path should not be a directory:" + file,file.isDirectory(),is(false));
|
||||
|
||||
String expectedSha1 = loadExpectedSha1Sum(sha1File);
|
||||
String actualSha1 = calculateSha1Sum(file);
|
||||
String expectedSha1 = Sha1Sum.loadSha1(sha1File);
|
||||
String actualSha1 = Sha1Sum.calculate(file);
|
||||
|
||||
Assert.assertThat("SHA1Sum of content: " + file,expectedSha1,equalToIgnoringCase(actualSha1));
|
||||
}
|
||||
|
||||
private String calculateSha1Sum(File file) throws IOException, NoSuchAlgorithmException
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||
try (FileInputStream fis = new FileInputStream(file);
|
||||
NoOpOutputStream noop = new NoOpOutputStream();
|
||||
DigestOutputStream digester = new DigestOutputStream(noop,digest))
|
||||
{
|
||||
IO.copy(fis,digester);
|
||||
return Hex.asHex(digest.digest());
|
||||
}
|
||||
}
|
||||
|
||||
private String loadExpectedSha1Sum(File sha1File) throws IOException
|
||||
{
|
||||
String contents = IO.readToString(sha1File);
|
||||
Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
|
||||
Matcher mat = pat.matcher(contents);
|
||||
Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
|
||||
return mat.group();
|
||||
}
|
||||
|
||||
@ClientEndpoint
|
||||
public static class ClientSocket
|
||||
{
|
||||
|
@ -317,32 +292,4 @@ public class StreamTest
|
|||
t.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoOpOutputStream extends OutputStream
|
||||
{
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,30 +154,33 @@ public abstract class CompressExtension extends AbstractExtension
|
|||
return;
|
||||
}
|
||||
byte[] output = new byte[DECOMPRESS_BUF_SIZE];
|
||||
|
||||
if (inflater.needsInput() && !supplyInput(inflater,buf))
|
||||
|
||||
while(buf.hasRemaining() && inflater.needsInput())
|
||||
{
|
||||
LOG.debug("Needed input, but no buffer could supply input");
|
||||
return;
|
||||
}
|
||||
|
||||
int read = 0;
|
||||
while ((read = inflater.inflate(output)) >= 0)
|
||||
{
|
||||
if (read == 0)
|
||||
if (!supplyInput(inflater,buf))
|
||||
{
|
||||
LOG.debug("Decompress: read 0 {}",toDetail(inflater));
|
||||
break;
|
||||
LOG.debug("Needed input, but no buffer could supply input");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
int read = 0;
|
||||
while ((read = inflater.inflate(output)) >= 0)
|
||||
{
|
||||
// do something with output
|
||||
if (LOG.isDebugEnabled())
|
||||
if (read == 0)
|
||||
{
|
||||
LOG.debug("Decompressed {} bytes: {}",read,toDetail(inflater));
|
||||
LOG.debug("Decompress: read 0 {}",toDetail(inflater));
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// do something with output
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Decompressed {} bytes: {}",read,toDetail(inflater));
|
||||
}
|
||||
|
||||
accumulator.copyChunk(output,0,read);
|
||||
}
|
||||
|
||||
accumulator.copyChunk(output,0,read);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.DigestOutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* Calculate the sha1sum for various content
|
||||
*/
|
||||
public class Sha1Sum
|
||||
{
|
||||
private static class NoOpOutputStream extends OutputStream
|
||||
{
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String calculate(File file) throws NoSuchAlgorithmException, IOException
|
||||
{
|
||||
return calculate(file.toPath());
|
||||
}
|
||||
|
||||
public static String calculate(Path path) throws NoSuchAlgorithmException, IOException
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||
try (InputStream in = Files.newInputStream(path,StandardOpenOption.READ);
|
||||
NoOpOutputStream noop = new NoOpOutputStream();
|
||||
DigestOutputStream digester = new DigestOutputStream(noop,digest))
|
||||
{
|
||||
IO.copy(in,digester);
|
||||
return Hex.asHex(digest.digest());
|
||||
}
|
||||
}
|
||||
|
||||
public static String calculate(byte[] buf) throws NoSuchAlgorithmException
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||
digest.update(buf);
|
||||
return Hex.asHex(digest.digest());
|
||||
}
|
||||
|
||||
public static String calculate(byte[] buf, int offset, int len) throws NoSuchAlgorithmException
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA1");
|
||||
digest.update(buf,offset,len);
|
||||
return Hex.asHex(digest.digest());
|
||||
}
|
||||
|
||||
public static String loadSha1(File sha1File) throws IOException
|
||||
{
|
||||
String contents = IO.readToString(sha1File);
|
||||
Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
|
||||
Matcher mat = pat.matcher(contents);
|
||||
Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
|
||||
return mat.group();
|
||||
}
|
||||
|
||||
}
|
|
@ -20,34 +20,90 @@ package org.eclipse.jetty.websocket.server;
|
|||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.common.frames.TextFrame;
|
||||
import org.eclipse.jetty.websocket.common.test.BlockheadClient;
|
||||
import org.eclipse.jetty.websocket.common.test.HttpResponse;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
|
||||
import org.eclipse.jetty.websocket.common.util.Sha1Sum;
|
||||
import org.eclipse.jetty.websocket.server.helper.CaptureSocket;
|
||||
import org.eclipse.jetty.websocket.server.helper.EchoServlet;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class PerMessageDeflateExtensionTest
|
||||
{
|
||||
private static SimpleServletServer server;
|
||||
|
||||
@BeforeClass
|
||||
public static void startServer() throws Exception
|
||||
private static enum TestCaseMessageSize
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.start();
|
||||
TINY(10),
|
||||
SMALL(1024),
|
||||
MEDIUM(10*1024),
|
||||
LARGE(100*1024),
|
||||
HUGE(1024*1024);
|
||||
|
||||
private int size;
|
||||
|
||||
private TestCaseMessageSize(int size)
|
||||
{
|
||||
this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
@Parameters(name = "{0} ({3}) (Input Buffer Size: {4} bytes)")
|
||||
public static List<Object[]> modes()
|
||||
{
|
||||
List<Object[]> modes = new ArrayList<>();
|
||||
|
||||
for(TestCaseMessageSize size: TestCaseMessageSize.values())
|
||||
{
|
||||
modes.add(new Object[] { "Normal HTTP/WS", false, "ws", size, -1 });
|
||||
modes.add(new Object[] { "Encrypted HTTPS/WSS", true, "wss", size, -1 });
|
||||
int altInputBufSize = 15*1024;
|
||||
modes.add(new Object[] { "Normal HTTP/WS", false, "ws", size, altInputBufSize });
|
||||
modes.add(new Object[] { "Encrypted HTTPS/WSS", true, "wss", size, altInputBufSize });
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stopServer()
|
||||
@Rule
|
||||
public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test");
|
||||
|
||||
private SimpleServletServer server;
|
||||
private String scheme;
|
||||
private int msgSize;
|
||||
private int inputBufferSize;
|
||||
|
||||
public PerMessageDeflateExtensionTest(String mode, boolean sslMode, String scheme, TestCaseMessageSize msgSize, int bufferSize) throws Exception
|
||||
{
|
||||
server = new SimpleServletServer(new EchoServlet());
|
||||
server.enableSsl(sslMode);
|
||||
server.start();
|
||||
|
||||
this.scheme = scheme;
|
||||
this.msgSize = msgSize.size;
|
||||
this.inputBufferSize = bufferSize;
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer()
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
@ -62,42 +118,84 @@ public class PerMessageDeflateExtensionTest
|
|||
Assume.assumeTrue("Server has permessage-deflate registered",
|
||||
server.getWebSocketServletFactory().getExtensionFactory().isAvailable("permessage-deflate"));
|
||||
|
||||
BlockheadClient client = new BlockheadClient(server.getServerUri());
|
||||
client.clearExtensions();
|
||||
client.addExtensions("permessage-deflate");
|
||||
client.setProtocols("echo");
|
||||
Assert.assertThat("server scheme",server.getServerUri().getScheme(),is(scheme));
|
||||
|
||||
int binBufferSize = (int) (msgSize * 1.5);
|
||||
|
||||
WebSocketPolicy serverPolicy = server.getWebSocketServletFactory().getPolicy();
|
||||
|
||||
// Ensure binBufferSize is sane (not smaller then other buffers)
|
||||
binBufferSize = Math.max(binBufferSize,serverPolicy.getMaxBinaryMessageSize());
|
||||
binBufferSize = Math.max(binBufferSize,serverPolicy.getMaxBinaryMessageBufferSize());
|
||||
binBufferSize = Math.max(binBufferSize,this.inputBufferSize);
|
||||
|
||||
serverPolicy.setMaxBinaryMessageSize(binBufferSize);
|
||||
serverPolicy.setMaxBinaryMessageBufferSize(binBufferSize);
|
||||
|
||||
WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool);
|
||||
WebSocketPolicy clientPolicy = client.getPolicy();
|
||||
clientPolicy.setMaxBinaryMessageSize(binBufferSize);
|
||||
clientPolicy.setMaxBinaryMessageBufferSize(binBufferSize);
|
||||
if (inputBufferSize > 0)
|
||||
{
|
||||
clientPolicy.setInputBufferSize(inputBufferSize);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
// Make sure the read times out if there are problems with the implementation
|
||||
client.setTimeout(1,TimeUnit.SECONDS);
|
||||
client.connect();
|
||||
client.sendStandardRequest();
|
||||
HttpResponse resp = client.expectUpgradeResponse();
|
||||
client.setMaxIdleTimeout(TimeUnit.SECONDS.toMillis(1));
|
||||
|
||||
Assert.assertThat("Response",resp.getExtensionsHeader(),containsString("permessage-deflate"));
|
||||
CaptureSocket clientSocket = new CaptureSocket();
|
||||
ClientUpgradeRequest request = new ClientUpgradeRequest();
|
||||
request.addExtensions("permessage-deflate");
|
||||
request.setSubProtocols("echo");
|
||||
|
||||
String msg = "Hello";
|
||||
Future<Session> fut = client.connect(clientSocket,server.getServerUri(),request);
|
||||
|
||||
// Wait for connect
|
||||
Session session = fut.get(3,TimeUnit.SECONDS);
|
||||
|
||||
assertThat("Response.extensions",getNegotiatedExtensionList(session),containsString("permessage-deflate"));
|
||||
|
||||
// Create message
|
||||
byte msg[] = new byte[msgSize];
|
||||
Random rand = new Random();
|
||||
rand.setSeed(8080);
|
||||
rand.nextBytes(msg);
|
||||
|
||||
// Calculate sha1
|
||||
String sha1 = Sha1Sum.calculate(msg);
|
||||
|
||||
// Client sends first message
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
session.getRemote().sendBytes(ByteBuffer.wrap(msg));
|
||||
|
||||
EventQueue<WebSocketFrame> frames = client.readFrames(1,1000,TimeUnit.MILLISECONDS);
|
||||
WebSocketFrame frame = frames.poll();
|
||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
||||
|
||||
// Client sends second message
|
||||
client.clearCaptured();
|
||||
msg = "There";
|
||||
client.write(new TextFrame().setPayload(msg));
|
||||
|
||||
frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||
frame = frames.poll();
|
||||
Assert.assertThat("TEXT.payload",frame.getPayloadAsUTF8(),is(msg.toString()));
|
||||
clientSocket.messages.awaitEventCount(1,1,TimeUnit.SECONDS);
|
||||
String echoMsg = clientSocket.messages.poll();
|
||||
Assert.assertThat("Echo'd Message",echoMsg,is("binary[sha1="+sha1+"]"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.close();
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private String getNegotiatedExtensionList(Session session)
|
||||
{
|
||||
StringBuilder actual = new StringBuilder();
|
||||
actual.append('[');
|
||||
|
||||
boolean delim = false;
|
||||
for (ExtensionConfig ext : session.getUpgradeResponse().getExtensions())
|
||||
{
|
||||
if (delim)
|
||||
actual.append(", ");
|
||||
actual.append(ext.getName());
|
||||
delim = true;
|
||||
}
|
||||
actual.append(']');
|
||||
|
||||
return actual.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.server.helper;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.common.util.Sha1Sum;
|
||||
|
||||
public class CaptureSocket extends WebSocketAdapter
|
||||
{
|
||||
|
@ -58,4 +60,18 @@ public class CaptureSocket extends WebSocketAdapter
|
|||
// System.out.printf("Received Message \"%s\" [size %d]%n", message, message.length());
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketBinary(byte[] payload, int offset, int len)
|
||||
{
|
||||
try
|
||||
{
|
||||
messages.add("binary[sha1="+Sha1Sum.calculate(payload,offset,len)+"]");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e)
|
||||
{
|
||||
messages.add("ERROR: Unable to caclulate Binary SHA1: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.concurrent.CountDownLatch;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -200,7 +201,20 @@ public class HttpClientLoadTest extends AbstractTest
|
|||
assertThat("Connection Leaks", connectionLeaks.get(), Matchers.is(0L));
|
||||
}
|
||||
|
||||
private void run(Random random, int iterations) throws InterruptedException
|
||||
@Test
|
||||
public void testConcurrent() throws Exception
|
||||
{
|
||||
start(new LoadHandler());
|
||||
|
||||
Random random = new Random();
|
||||
int runs = 1;
|
||||
int iterations = 256;
|
||||
IntStream.range(0, 16).parallel().forEach(i ->
|
||||
IntStream.range(0, runs).forEach(j ->
|
||||
run(random, iterations)));
|
||||
}
|
||||
|
||||
private void run(Random random, int iterations)
|
||||
{
|
||||
CountDownLatch latch = new CountDownLatch(iterations);
|
||||
List<String> failures = new ArrayList<>();
|
||||
|
@ -222,7 +236,7 @@ public class HttpClientLoadTest extends AbstractTest
|
|||
test(random, latch, failures);
|
||||
// test("http", "localhost", "GET", false, false, 64 * 1024, false, latch, failures);
|
||||
}
|
||||
Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(await(latch, iterations, TimeUnit.SECONDS));
|
||||
long end = System.nanoTime();
|
||||
task.cancel();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
|
@ -234,7 +248,7 @@ public class HttpClientLoadTest extends AbstractTest
|
|||
Assert.assertTrue(failures.toString(), failures.isEmpty());
|
||||
}
|
||||
|
||||
private void test(Random random, final CountDownLatch latch, final List<String> failures) throws InterruptedException
|
||||
private void test(Random random, final CountDownLatch latch, final List<String> failures)
|
||||
{
|
||||
// Choose a random destination
|
||||
String host = random.nextBoolean() ? "localhost" : "127.0.0.1";
|
||||
|
@ -257,7 +271,7 @@ public class HttpClientLoadTest extends AbstractTest
|
|||
test(ssl ? "https" : "http", host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
|
||||
}
|
||||
|
||||
private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures) throws InterruptedException
|
||||
private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures)
|
||||
{
|
||||
Request request = client.newRequest(host, connector.getLocalPort())
|
||||
.scheme(scheme)
|
||||
|
@ -318,7 +332,19 @@ public class HttpClientLoadTest extends AbstractTest
|
|||
latch.countDown();
|
||||
}
|
||||
});
|
||||
requestLatch.await(5, TimeUnit.SECONDS);
|
||||
await(requestLatch, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private boolean await(CountDownLatch latch, long time, TimeUnit unit)
|
||||
{
|
||||
try
|
||||
{
|
||||
return latch.await(time, unit);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadHandler extends AbstractHandler
|
||||
|
|
Loading…
Reference in New Issue