Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x
Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
commit
0f48708922
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.http.pathmap;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPathSpec implements PathSpec
|
||||
{
|
||||
@Override
|
||||
public int compareTo(PathSpec other)
|
||||
{
|
||||
// Grouping (increasing)
|
||||
int diff = getGroup().ordinal() - other.getGroup().ordinal();
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
// Spec Length (decreasing)
|
||||
diff = other.getSpecLength() - getSpecLength();
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
// Path Spec Name (alphabetical)
|
||||
return getDeclaration().compareTo(other.getDeclaration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
|
||||
return compareTo((AbstractPathSpec)obj) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode()
|
||||
{
|
||||
return Objects.hash(getDeclaration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s@%s{%s}", getClass().getSimpleName(), Integer.toHexString(hashCode()), getDeclaration());
|
||||
}
|
||||
}
|
|
@ -261,16 +261,21 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
|||
@SuppressWarnings("incomplete-switch")
|
||||
public boolean remove(PathSpec pathSpec)
|
||||
{
|
||||
String prefix = pathSpec.getPrefix();
|
||||
String suffix = pathSpec.getSuffix();
|
||||
switch (pathSpec.getGroup())
|
||||
{
|
||||
case EXACT:
|
||||
_exactMap.remove(pathSpec.getPrefix());
|
||||
if (prefix != null)
|
||||
_exactMap.remove(prefix);
|
||||
break;
|
||||
case PREFIX_GLOB:
|
||||
_prefixMap.remove(pathSpec.getPrefix());
|
||||
if (prefix != null)
|
||||
_prefixMap.remove(prefix);
|
||||
break;
|
||||
case SUFFIX_GLOB:
|
||||
_suffixMap.remove(pathSpec.getSuffix());
|
||||
if (suffix != null)
|
||||
_suffixMap.remove(suffix);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.eclipse.jetty.http.pathmap;
|
|||
|
||||
/**
|
||||
* A path specification is a URI path template that can be matched against.
|
||||
* <p>
|
||||
* Implementors <i>must</i> override {@link Object#equals(Object)} and {@link Object#hashCode()}.
|
||||
*/
|
||||
public interface PathSpec extends Comparable<PathSpec>
|
||||
{
|
||||
|
@ -90,20 +92,4 @@ public interface PathSpec extends Comparable<PathSpec>
|
|||
* @return true if the path matches this path spec, false otherwise
|
||||
*/
|
||||
boolean matches(String path);
|
||||
|
||||
default int compareTo(PathSpec other)
|
||||
{
|
||||
// Grouping (increasing)
|
||||
int diff = getGroup().ordinal() - other.getGroup().ordinal();
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
// Spec Length (decreasing)
|
||||
diff = other.getSpecLength() - getSpecLength();
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
|
||||
// Path Spec Name (alphabetical)
|
||||
return getDeclaration().compareTo(other.getDeclaration());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.eclipse.jetty.http.pathmap;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RegexPathSpec implements PathSpec
|
||||
public class RegexPathSpec extends AbstractPathSpec
|
||||
{
|
||||
private final String _declaration;
|
||||
private final PathSpecGroup _group;
|
||||
|
|
|
@ -23,7 +23,7 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ServletPathSpec implements PathSpec
|
||||
public class ServletPathSpec extends AbstractPathSpec
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServletPathSpec.class);
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ import org.slf4j.LoggerFactory;
|
|||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6570">URI Templates (Level 1)</a>
|
||||
*/
|
||||
public class UriTemplatePathSpec implements PathSpec
|
||||
public class UriTemplatePathSpec extends AbstractPathSpec
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UriTemplatePathSpec.class);
|
||||
|
||||
|
@ -287,7 +287,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
}
|
||||
else
|
||||
{
|
||||
return PathSpec.super.compareTo(other);
|
||||
return super.compareTo(other);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,11 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -302,4 +305,160 @@ public class PathMappingsTest
|
|||
new ServletPathSpec(str);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPutRejectsDuplicates()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
assertThat(p.put(new UriTemplatePathSpec("/a/{var1}/c"), "resourceA"), is(true));
|
||||
assertThat(p.put(new UriTemplatePathSpec("/a/{var2}/c"), "resourceAA"), is(false));
|
||||
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceB"), is(true));
|
||||
assertThat(p.put(new UriTemplatePathSpec("/a/b/c"), "resourceBB"), is(false));
|
||||
assertThat(p.put(new ServletPathSpec("/a/b/c"), "resourceBB"), is(false));
|
||||
assertThat(p.put(new RegexPathSpec("/a/b/c"), "resourceBB"), is(false));
|
||||
|
||||
assertThat(p.put(new ServletPathSpec("/*"), "resourceC"), is(true));
|
||||
assertThat(p.put(new RegexPathSpec("/(.*)"), "resourceCC"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetUriTemplatePathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
p.put(new UriTemplatePathSpec("/a/{var1}/c"), "resourceA");
|
||||
p.put(new UriTemplatePathSpec("/a/b/c"), "resourceB");
|
||||
|
||||
assertThat(p.get(new UriTemplatePathSpec("/a/{var1}/c")), equalTo("resourceA"));
|
||||
assertThat(p.get(new UriTemplatePathSpec("/a/{foo}/c")), equalTo("resourceA"));
|
||||
assertThat(p.get(new UriTemplatePathSpec("/a/b/c")), equalTo("resourceB"));
|
||||
assertThat(p.get(new UriTemplatePathSpec("/a/d/c")), nullValue());
|
||||
assertThat(p.get(new RegexPathSpec("/a/b/c")), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRegexPathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
p.put(new RegexPathSpec("/a/b/c"), "resourceA");
|
||||
p.put(new RegexPathSpec("/(.*)/b/c"), "resourceB");
|
||||
p.put(new RegexPathSpec("/a/(.*)/c"), "resourceC");
|
||||
p.put(new RegexPathSpec("/a/b/(.*)"), "resourceD");
|
||||
|
||||
assertThat(p.get(new RegexPathSpec("/a/(.*)/c")), equalTo("resourceC"));
|
||||
assertThat(p.get(new RegexPathSpec("/a/b/c")), equalTo("resourceA"));
|
||||
assertThat(p.get(new RegexPathSpec("/(.*)/b/c")), equalTo("resourceB"));
|
||||
assertThat(p.get(new RegexPathSpec("/a/b/(.*)")), equalTo("resourceD"));
|
||||
assertThat(p.get(new RegexPathSpec("/a/d/c")), nullValue());
|
||||
assertThat(p.get(new ServletPathSpec("/a/b/c")), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetServletPathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
p.put(new ServletPathSpec("/"), "resourceA");
|
||||
p.put(new ServletPathSpec("/*"), "resourceB");
|
||||
p.put(new ServletPathSpec("/a/*"), "resourceC");
|
||||
p.put(new ServletPathSpec("*.do"), "resourceD");
|
||||
|
||||
assertThat(p.get(new ServletPathSpec("/")), equalTo("resourceA"));
|
||||
assertThat(p.get(new ServletPathSpec("/*")), equalTo("resourceB"));
|
||||
assertThat(p.get(new ServletPathSpec("/a/*")), equalTo("resourceC"));
|
||||
assertThat(p.get(new ServletPathSpec("*.do")), equalTo("resourceD"));
|
||||
assertThat(p.get(new ServletPathSpec("*.gz")), nullValue());
|
||||
assertThat(p.get(new ServletPathSpec("/a/b/*")), nullValue());
|
||||
assertThat(p.get(new ServletPathSpec("/a/d/c")), nullValue());
|
||||
assertThat(p.get(new RegexPathSpec("/a/b/c")), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveUriTemplatePathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new UriTemplatePathSpec("/a/{var1}/c"), "resourceA");
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/{var1}/c")), is(true));
|
||||
|
||||
p.put(new UriTemplatePathSpec("/a/{var1}/c"), "resourceA");
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/{b}/c")), is(true));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/{b}/c")), is(false));
|
||||
|
||||
p.put(new UriTemplatePathSpec("/{var1}/b/c"), "resourceA");
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/{a}/b/c")), is(true));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/{a}/b/c")), is(false));
|
||||
|
||||
p.put(new UriTemplatePathSpec("/a/b/{var1}"), "resourceA");
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/{c}")), is(true));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/{c}")), is(false));
|
||||
|
||||
p.put(new UriTemplatePathSpec("/{var1}/{var2}/{var3}"), "resourceA");
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/{a}/{b}/{c}")), is(true));
|
||||
assertThat(p.remove(new UriTemplatePathSpec("/{a}/{b}/{c}")), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveRegexPathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new RegexPathSpec("/a/(.*)/c"), "resourceA");
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/(.*)/c")), is(true));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/(.*)/c")), is(false));
|
||||
|
||||
p.put(new RegexPathSpec("/(.*)/b/c"), "resourceA");
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new RegexPathSpec("/(.*)/b/c")), is(true));
|
||||
assertThat(p.remove(new RegexPathSpec("/(.*)/b/c")), is(false));
|
||||
|
||||
p.put(new RegexPathSpec("/a/b/(.*)"), "resourceA");
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/(.*)")), is(true));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/(.*)")), is(false));
|
||||
|
||||
p.put(new RegexPathSpec("/a/b/c"), "resourceA");
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/d")), is(false));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/c")), is(true));
|
||||
assertThat(p.remove(new RegexPathSpec("/a/b/c")), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveServletPathSpec()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new ServletPathSpec("/a/*"), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/*")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/*")), is(false));
|
||||
|
||||
p.put(new ServletPathSpec("/a/b/*"), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/c")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/*")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/*")), is(false));
|
||||
|
||||
p.put(new ServletPathSpec("*.do"), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("*.gz")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("*.do")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("*.do")), is(false));
|
||||
|
||||
p.put(new ServletPathSpec("/"), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("/a")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("/")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("/")), is(false));
|
||||
|
||||
p.put(new ServletPathSpec(""), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("/")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("")), is(false));
|
||||
|
||||
p.put(new ServletPathSpec("/a/b/c"), "resourceA");
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/d")), is(false));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/c")), is(true));
|
||||
assertThat(p.remove(new ServletPathSpec("/a/b/c")), is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ package org.eclipse.jetty.http.pathmap;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class RegexPathSpecTest
|
||||
|
@ -132,4 +134,14 @@ public class RegexPathSpecTest
|
|||
assertNotMatches(spec, "/aa/bb");
|
||||
assertNotMatches(spec, "/aa/bb.do/more");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEquals()
|
||||
{
|
||||
assertThat(new RegexPathSpec("^(.*).do$"), equalTo(new RegexPathSpec("^(.*).do$")));
|
||||
assertThat(new RegexPathSpec("/foo"), equalTo(new RegexPathSpec("/foo")));
|
||||
assertThat(new RegexPathSpec("^(.*).do$"), not(equalTo(new RegexPathSpec("^(.*).gz$"))));
|
||||
assertThat(new RegexPathSpec("^(.*).do$"), not(equalTo(new RegexPathSpec("^.*.do$"))));
|
||||
assertThat(new RegexPathSpec("/foo"), not(equalTo(new ServletPathSpec("/foo"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ package org.eclipse.jetty.http.pathmap;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -187,4 +189,17 @@ public class ServletPathSpecTest
|
|||
|
||||
assertEquals(null, spec.getPathInfo("/downloads/distribution.tar.gz"), "Spec.pathInfo");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEquals()
|
||||
{
|
||||
assertThat(new ServletPathSpec("*.gz"), equalTo(new ServletPathSpec("*.gz")));
|
||||
assertThat(new ServletPathSpec("/foo"), equalTo(new ServletPathSpec("/foo")));
|
||||
assertThat(new ServletPathSpec("/foo/bar"), equalTo(new ServletPathSpec("/foo/bar")));
|
||||
assertThat(new ServletPathSpec("*.gz"), not(equalTo(new ServletPathSpec("*.do"))));
|
||||
assertThat(new ServletPathSpec("/foo"), not(equalTo(new ServletPathSpec("/bar"))));
|
||||
assertThat(new ServletPathSpec("/bar/foo"), not(equalTo(new ServletPathSpec("/foo/bar"))));
|
||||
assertThat(new ServletPathSpec("/foo"), not(equalTo(new RegexPathSpec("/foo"))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ import java.util.Map;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
@ -281,4 +283,15 @@ public class UriTemplatePathSpecTest
|
|||
assertThat("Spec.pathParams.size", mapped.size(), is(1));
|
||||
assertEquals("a", mapped.get("var1"), "Spec.pathParams[var1]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEquals()
|
||||
{
|
||||
assertThat(new UriTemplatePathSpec("/{var1}"), equalTo(new UriTemplatePathSpec("/{var1}")));
|
||||
assertThat(new UriTemplatePathSpec("/{var1}"), equalTo(new UriTemplatePathSpec("/{var2}")));
|
||||
assertThat(new UriTemplatePathSpec("/{var1}/{var2}"), equalTo(new UriTemplatePathSpec("/{var2}/{var1}")));
|
||||
assertThat(new UriTemplatePathSpec("/{var1}"), not(equalTo(new UriTemplatePathSpec("/{var1}/{var2}"))));
|
||||
assertThat(new UriTemplatePathSpec("/a/b/c"), not(equalTo(new UriTemplatePathSpec("/a/{var}/c"))));
|
||||
assertThat(new UriTemplatePathSpec("/foo"), not(equalTo(new ServletPathSpec("/foo"))));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
private final HttpParser _parser;
|
||||
private final AtomicInteger _contentBufferReferences = new AtomicInteger();
|
||||
private volatile ByteBuffer _requestBuffer = null;
|
||||
private volatile ByteBuffer _chunk = null;
|
||||
private final BlockingReadCallback _blockingReadCallback = new BlockingReadCallback();
|
||||
private final AsyncReadCallback _asyncReadCallback = new AsyncReadCallback();
|
||||
private final SendCallback _sendCallback = new SendCallback();
|
||||
|
@ -464,11 +463,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_parser.close();
|
||||
}
|
||||
|
||||
// Not in a race here with onFillable, because it has given up control before calling handle.
|
||||
// in a slight race with #completed, but not sure what to do with that anyway.
|
||||
if (_chunk != null)
|
||||
_bufferPool.release(_chunk);
|
||||
_chunk = null;
|
||||
_generator.reset();
|
||||
|
||||
// if we are not called from the onfillable thread, schedule completion
|
||||
|
@ -718,6 +712,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
private boolean _lastContent;
|
||||
private Callback _callback;
|
||||
private ByteBuffer _header;
|
||||
private ByteBuffer _chunk;
|
||||
private boolean _shutdownOut;
|
||||
|
||||
private SendCallback()
|
||||
|
@ -763,10 +758,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
throw new IllegalStateException();
|
||||
|
||||
boolean useDirectByteBuffers = isUseOutputDirectByteBuffers();
|
||||
ByteBuffer chunk = _chunk;
|
||||
while (true)
|
||||
{
|
||||
HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, chunk, _content, _lastContent);
|
||||
HttpGenerator.Result result = _generator.generateResponse(_info, _head, _header, _chunk, _content, _lastContent);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("generate: {} for {} ({},{},{})@{}",
|
||||
result,
|
||||
|
@ -788,23 +782,21 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
}
|
||||
case HEADER_OVERFLOW:
|
||||
{
|
||||
int capacity = _header.capacity();
|
||||
_bufferPool.release(_header);
|
||||
if (capacity >= _config.getResponseHeaderSize())
|
||||
if (_header.capacity() >= _config.getResponseHeaderSize())
|
||||
throw new BadMessageException(HttpStatus.INTERNAL_SERVER_ERROR_500, "Response header too large");
|
||||
releaseHeader();
|
||||
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
|
||||
continue;
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
{
|
||||
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
|
||||
_chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, useDirectByteBuffers);
|
||||
continue;
|
||||
}
|
||||
case NEED_CHUNK_TRAILER:
|
||||
{
|
||||
if (_chunk != null)
|
||||
_bufferPool.release(_chunk);
|
||||
chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
|
||||
releaseChunk();
|
||||
_chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), useDirectByteBuffers);
|
||||
continue;
|
||||
}
|
||||
case FLUSH:
|
||||
|
@ -812,7 +804,7 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
// Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content
|
||||
if (_head || _generator.isNoContent())
|
||||
{
|
||||
BufferUtil.clear(chunk);
|
||||
BufferUtil.clear(_chunk);
|
||||
BufferUtil.clear(_content);
|
||||
}
|
||||
|
||||
|
@ -823,10 +815,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
gatherWrite += 4;
|
||||
bytes += _header.remaining();
|
||||
}
|
||||
if (BufferUtil.hasContent(chunk))
|
||||
if (BufferUtil.hasContent(_chunk))
|
||||
{
|
||||
gatherWrite += 2;
|
||||
bytes += chunk.remaining();
|
||||
bytes += _chunk.remaining();
|
||||
}
|
||||
if (BufferUtil.hasContent(_content))
|
||||
{
|
||||
|
@ -837,10 +829,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
switch (gatherWrite)
|
||||
{
|
||||
case 7:
|
||||
getEndPoint().write(this, _header, chunk, _content);
|
||||
getEndPoint().write(this, _header, _chunk, _content);
|
||||
break;
|
||||
case 6:
|
||||
getEndPoint().write(this, _header, chunk);
|
||||
getEndPoint().write(this, _header, _chunk);
|
||||
break;
|
||||
case 5:
|
||||
getEndPoint().write(this, _header, _content);
|
||||
|
@ -849,10 +841,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
getEndPoint().write(this, _header);
|
||||
break;
|
||||
case 3:
|
||||
getEndPoint().write(this, chunk, _content);
|
||||
getEndPoint().write(this, _chunk, _content);
|
||||
break;
|
||||
case 2:
|
||||
getEndPoint().write(this, chunk);
|
||||
getEndPoint().write(this, _chunk);
|
||||
break;
|
||||
case 1:
|
||||
getEndPoint().write(this, _content);
|
||||
|
@ -896,10 +888,23 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_callback = null;
|
||||
_info = null;
|
||||
_content = null;
|
||||
releaseHeader();
|
||||
releaseChunk();
|
||||
return complete;
|
||||
}
|
||||
|
||||
private void releaseHeader()
|
||||
{
|
||||
if (_header != null)
|
||||
_bufferPool.release(_header);
|
||||
_header = null;
|
||||
return complete;
|
||||
}
|
||||
|
||||
private void releaseChunk()
|
||||
{
|
||||
if (_chunk != null)
|
||||
_bufferPool.release(_chunk);
|
||||
_chunk = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
//
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
//
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
//
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class LargeHeaderTest
|
||||
{
|
||||
private Server server;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
|
||||
HttpConfiguration config = new HttpConfiguration();
|
||||
HttpConnectionFactory http = new HttpConnectionFactory(config);
|
||||
|
||||
ServerConnector connector = new ServerConnector(server, http);
|
||||
connector.setPort(0);
|
||||
connector.setIdleTimeout(5000);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.setErrorHandler(new ErrorHandler());
|
||||
|
||||
server.setHandler(new AbstractHandler()
|
||||
{
|
||||
final String largeHeaderValue;
|
||||
|
||||
{
|
||||
byte[] bytes = new byte[8 * 1024];
|
||||
Arrays.fill(bytes, (byte)'X');
|
||||
largeHeaderValue = "LargeHeaderOver8k-" + new String(bytes, UTF_8) + "_Z_";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE.toString(), MimeTypes.Type.TEXT_HTML.toString());
|
||||
response.setHeader("LongStr", largeHeaderValue);
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write("<html><h1>FOO</h1></html>");
|
||||
writer.flush();
|
||||
response.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void teardown()
|
||||
{
|
||||
LifeCycle.stop(server);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLargeHeader() throws Throwable
|
||||
{
|
||||
final Logger CLIENTLOG = Log.getLogger(LargeHeaderTest.class).getLogger(".client");
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(8);
|
||||
|
||||
int localPort = server.getURI().getPort();
|
||||
String rawRequest = "GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost:" + localPort + "\r\n" +
|
||||
"\r\n";
|
||||
|
||||
Throwable issues = new Throwable();
|
||||
|
||||
for (int i = 0; i < 500; ++i)
|
||||
{
|
||||
executorService.submit(() ->
|
||||
{
|
||||
try (Socket client = new Socket("localhost", localPort);
|
||||
OutputStream output = client.getOutputStream();
|
||||
InputStream input = client.getInputStream())
|
||||
{
|
||||
output.write(rawRequest.getBytes(UTF_8));
|
||||
output.flush();
|
||||
|
||||
String rawResponse = IO.toString(input, UTF_8);
|
||||
HttpTester.Response response = HttpTester.parseResponse(rawResponse);
|
||||
assertThat(response.getStatus(), is(500));
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
CLIENTLOG.warn("Client Issue", t);
|
||||
issues.addSuppressed(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
executorService.awaitTermination(5, TimeUnit.SECONDS);
|
||||
if (issues.getSuppressed().length > 0)
|
||||
{
|
||||
throw issues;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.server.session;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -255,8 +257,12 @@ public class TestFileSessions extends AbstractTestBase
|
|||
FileTestHelper.createFile(foreignNeverExpired);
|
||||
FileTestHelper.assertFileExists(foreignNeverExpired, true);
|
||||
|
||||
//sweep
|
||||
((FileSessionDataStore)store).sweepDisk();
|
||||
//sweep - we're expecting a debug log with exception stacktrace due to file named
|
||||
//nonNumber__0.0.0.0_spuriousFile so suppress it
|
||||
try (StacklessLogging stackless = new StacklessLogging(Log.getLogger("org.eclipse.jetty.server.session")))
|
||||
{
|
||||
((FileSessionDataStore)store).sweepDisk();
|
||||
}
|
||||
|
||||
//check results
|
||||
FileTestHelper.assertSessionExists("sessiona", false);
|
||||
|
|
Loading…
Reference in New Issue