Merge branch 'master' into jetty-9.4.x-Feature
This commit is contained in:
commit
ea1deda4f9
|
@ -21,6 +21,8 @@ package org.eclipse.jetty.client;
|
||||||
import org.eclipse.jetty.client.api.Request;
|
import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.api.Result;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A protocol handler that handles redirect status codes 301, 302, 303, 307 and 308.</p>
|
* <p>A protocol handler that handles redirect status codes 301, 302, 303, 307 and 308.</p>
|
||||||
|
@ -54,6 +56,14 @@ public class RedirectProtocolHandler extends Response.Listener.Adapter implement
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onHeader(Response response, HttpField field)
|
||||||
|
{
|
||||||
|
// Avoid that the content is decoded, which could generate
|
||||||
|
// errors, since we are discarding the content anyway.
|
||||||
|
return field.getHeader() != HttpHeader.CONTENT_ENCODING;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.UnresolvedAddressException;
|
import java.nio.channels.UnresolvedAddressException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -44,7 +45,6 @@ import org.eclipse.jetty.toolchain.test.IO;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -55,15 +55,11 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
super(sslContextFactory);
|
super(sslContextFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
|
||||||
public void prepare() throws Exception
|
|
||||||
{
|
|
||||||
start(new RedirectHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_303() throws Exception
|
public void test_303() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/done")
|
.path("/303/localhost/done")
|
||||||
|
@ -77,6 +73,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_303_302() throws Exception
|
public void test_303_302() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/302/localhost/done")
|
.path("/303/localhost/302/localhost/done")
|
||||||
|
@ -90,6 +88,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_303_302_OnDifferentDestinations() throws Exception
|
public void test_303_302_OnDifferentDestinations() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/127.0.0.1/302/localhost/done")
|
.path("/303/127.0.0.1/302/localhost/done")
|
||||||
|
@ -103,6 +103,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_301() throws Exception
|
public void test_301() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.method(HttpMethod.HEAD)
|
.method(HttpMethod.HEAD)
|
||||||
|
@ -117,6 +119,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_301_WithWrongMethod() throws Exception
|
public void test_301_WithWrongMethod() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
@ -140,6 +144,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_307_WithRequestContent() throws Exception
|
public void test_307_WithRequestContent() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7};
|
||||||
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
|
@ -157,6 +163,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testMaxRedirections() throws Exception
|
public void testMaxRedirections() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
client.setMaxRedirects(1);
|
client.setMaxRedirects(1);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -181,6 +188,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void test_303_WithConnectionClose_WithBigRequest() throws Exception
|
public void test_303_WithConnectionClose_WithBigRequest() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/done?close=true")
|
.path("/303/localhost/done?close=true")
|
||||||
|
@ -194,6 +203,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testDontFollowRedirects() throws Exception
|
public void testDontFollowRedirects() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.followRedirects(false)
|
.followRedirects(false)
|
||||||
|
@ -208,6 +219,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testRelativeLocation() throws Exception
|
public void testRelativeLocation() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/done?relative=true")
|
.path("/303/localhost/done?relative=true")
|
||||||
|
@ -221,6 +234,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testAbsoluteURIPathWithSpaces() throws Exception
|
public void testAbsoluteURIPathWithSpaces() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/a+space?decode=true")
|
.path("/303/localhost/a+space?decode=true")
|
||||||
|
@ -234,6 +249,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testRelativeURIPathWithSpaces() throws Exception
|
public void testRelativeURIPathWithSpaces() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
Response response = client.newRequest("localhost", connector.getLocalPort())
|
Response response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/303/localhost/a+space?relative=true&decode=true")
|
.path("/303/localhost/a+space?relative=true&decode=true")
|
||||||
|
@ -247,7 +264,6 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testRedirectWithWrongScheme() throws Exception
|
public void testRedirectWithWrongScheme() throws Exception
|
||||||
{
|
{
|
||||||
dispose();
|
|
||||||
start(new AbstractHandler()
|
start(new AbstractHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -264,14 +280,10 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.path("/path")
|
.path("/path")
|
||||||
.timeout(5, TimeUnit.SECONDS)
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
.send(new Response.CompleteListener()
|
.send(result ->
|
||||||
{
|
{
|
||||||
@Override
|
Assert.assertTrue(result.isFailed());
|
||||||
public void onComplete(Result result)
|
latch.countDown();
|
||||||
{
|
|
||||||
Assert.assertTrue(result.isFailed());
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
@ -281,6 +293,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
public void testRedirectFailed() throws Exception
|
public void testRedirectFailed() throws Exception
|
||||||
{
|
{
|
||||||
// TODO this test is failing with timout after an ISP upgrade?? DNS dependent?
|
// TODO this test is failing with timout after an ISP upgrade?? DNS dependent?
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
@ -370,6 +384,7 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testHttpRedirector() throws Exception
|
public void testHttpRedirector() throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
final HttpRedirector redirector = new HttpRedirector(client);
|
final HttpRedirector redirector = new HttpRedirector(client);
|
||||||
|
|
||||||
org.eclipse.jetty.client.api.Request request1 = client.newRequest("localhost", connector.getLocalPort())
|
org.eclipse.jetty.client.api.Request request1 = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
@ -390,20 +405,52 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
Assert.assertTrue(redirector.isRedirect(response2));
|
Assert.assertTrue(redirector.isRedirect(response2));
|
||||||
|
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
final CountDownLatch latch = new CountDownLatch(1);
|
||||||
redirector.redirect(request2, response2, new Response.CompleteListener()
|
redirector.redirect(request2, response2, r ->
|
||||||
{
|
{
|
||||||
@Override
|
Response response3 = r.getResponse();
|
||||||
public void onComplete(Result result)
|
Assert.assertEquals(200, response3.getStatus());
|
||||||
{
|
Assert.assertFalse(redirector.isRedirect(response3));
|
||||||
Response response3 = result.getResponse();
|
latch.countDown();
|
||||||
Assert.assertEquals(200, response3.getStatus());
|
|
||||||
Assert.assertFalse(redirector.isRedirect(response3));
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRedirectWithCorruptedBody() throws Exception
|
||||||
|
{
|
||||||
|
byte[] bytes = "ok".getBytes(StandardCharsets.UTF_8);
|
||||||
|
start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
if (target.startsWith("/redirect"))
|
||||||
|
{
|
||||||
|
response.setStatus(HttpStatus.SEE_OTHER_303);
|
||||||
|
response.setHeader(HttpHeader.LOCATION.asString(), scheme + "://localhost:" + connector.getLocalPort() + "/ok");
|
||||||
|
// Say that we send gzipped content, but actually don't.
|
||||||
|
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(), "gzip");
|
||||||
|
response.getOutputStream().write("redirect".getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response.setStatus(HttpStatus.OK_200);
|
||||||
|
response.getOutputStream().write(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
|
||||||
|
.scheme(scheme)
|
||||||
|
.path("/redirect")
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(200, response.getStatus());
|
||||||
|
Assert.assertArrayEquals(bytes, response.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
private void testSameMethodRedirect(final HttpMethod method, int redirectCode) throws Exception
|
private void testSameMethodRedirect(final HttpMethod method, int redirectCode) throws Exception
|
||||||
{
|
{
|
||||||
testMethodRedirect(method, method, redirectCode);
|
testMethodRedirect(method, method, redirectCode);
|
||||||
|
@ -416,6 +463,8 @@ public class HttpClientRedirectTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
private void testMethodRedirect(final HttpMethod requestMethod, final HttpMethod redirectMethod, int redirectCode) throws Exception
|
private void testMethodRedirect(final HttpMethod requestMethod, final HttpMethod redirectMethod, int redirectCode) throws Exception
|
||||||
{
|
{
|
||||||
|
start(new RedirectHandler());
|
||||||
|
|
||||||
final AtomicInteger passes = new AtomicInteger();
|
final AtomicInteger passes = new AtomicInteger();
|
||||||
client.getRequestListeners().add(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
client.getRequestListeners().add(new org.eclipse.jetty.client.api.Request.Listener.Adapter()
|
||||||
{
|
{
|
||||||
|
|
|
@ -78,8 +78,8 @@ import static org.eclipse.jetty.http.HttpTokens.TAB;
|
||||||
* <dl>
|
* <dl>
|
||||||
* <dt>RFC7230</dt><dd>(default) Compliance with RFC7230</dd>
|
* <dt>RFC7230</dt><dd>(default) Compliance with RFC7230</dd>
|
||||||
* <dt>RFC2616</dt><dd>Wrapped headers and HTTP/0.9 supported</dd>
|
* <dt>RFC2616</dt><dd>Wrapped headers and HTTP/0.9 supported</dd>
|
||||||
* <dt>LEGACY</dt><dd>(aka STRICT) Adherence to Servlet Specification requirement for
|
* <dt>LEGACY</dt><dd>(aka STRICT) Adherence to Servlet Specification requirement for
|
||||||
* exact case of header names, bypassing the header caches, which are case insensitive,
|
* exact case of header names, bypassing the header caches, which are case insensitive,
|
||||||
* otherwise equivalent to RFC2616</dd>
|
* otherwise equivalent to RFC2616</dd>
|
||||||
* </dl>
|
* </dl>
|
||||||
* @see <a href="http://tools.ietf.org/html/rfc7230">RFC 7230</a>
|
* @see <a href="http://tools.ietf.org/html/rfc7230">RFC 7230</a>
|
||||||
|
@ -221,10 +221,10 @@ public class HttpParser
|
||||||
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
|
CACHE.put(new HttpField(HttpHeader.AUTHORIZATION,(String)null));
|
||||||
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
|
CACHE.put(new HttpField(HttpHeader.COOKIE,(String)null));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpCompliance compliance()
|
private static HttpCompliance compliance()
|
||||||
{
|
{
|
||||||
Boolean strict = Boolean.getBoolean(__STRICT);
|
Boolean strict = Boolean.getBoolean(__STRICT);
|
||||||
return strict?HttpCompliance.LEGACY:HttpCompliance.RFC7230;
|
return strict?HttpCompliance.LEGACY:HttpCompliance.RFC7230;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ public class HttpParser
|
||||||
{
|
{
|
||||||
this(handler,maxHeaderBytes,strict?HttpCompliance.LEGACY:compliance());
|
this(handler,maxHeaderBytes,strict?HttpCompliance.LEGACY:compliance());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
|
public HttpParser(ResponseHandler handler,int maxHeaderBytes,boolean strict)
|
||||||
|
@ -271,7 +271,7 @@ public class HttpParser
|
||||||
{
|
{
|
||||||
this(handler,-1,compliance);
|
this(handler,-1,compliance);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
public HttpParser(RequestHandler handler,int maxHeaderBytes,HttpCompliance compliance)
|
public HttpParser(RequestHandler handler,int maxHeaderBytes,HttpCompliance compliance)
|
||||||
{
|
{
|
||||||
|
@ -841,17 +841,13 @@ public class HttpParser
|
||||||
switch (_header)
|
switch (_header)
|
||||||
{
|
{
|
||||||
case CONTENT_LENGTH:
|
case CONTENT_LENGTH:
|
||||||
if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
|
if (_endOfContent == EndOfContent.CONTENT_LENGTH)
|
||||||
{
|
{
|
||||||
try
|
throw new BadMessageException(HttpStatus.BAD_REQUEST_400, "Duplicate Content-Length");
|
||||||
{
|
}
|
||||||
_contentLength=Long.parseLong(_valueString);
|
else if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
|
||||||
}
|
{
|
||||||
catch(NumberFormatException e)
|
_contentLength=convertContentLength(_valueString);
|
||||||
{
|
|
||||||
LOG.ignore(e);
|
|
||||||
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
|
|
||||||
}
|
|
||||||
if (_contentLength <= 0)
|
if (_contentLength <= 0)
|
||||||
_endOfContent=EndOfContent.NO_CONTENT;
|
_endOfContent=EndOfContent.NO_CONTENT;
|
||||||
else
|
else
|
||||||
|
@ -861,15 +857,16 @@ public class HttpParser
|
||||||
|
|
||||||
case TRANSFER_ENCODING:
|
case TRANSFER_ENCODING:
|
||||||
if (_value==HttpHeaderValue.CHUNKED)
|
if (_value==HttpHeaderValue.CHUNKED)
|
||||||
|
{
|
||||||
_endOfContent=EndOfContent.CHUNKED_CONTENT;
|
_endOfContent=EndOfContent.CHUNKED_CONTENT;
|
||||||
|
_contentLength=-1;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
|
if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
|
||||||
_endOfContent=EndOfContent.CHUNKED_CONTENT;
|
_endOfContent=EndOfContent.CHUNKED_CONTENT;
|
||||||
else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
|
else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
|
||||||
{
|
|
||||||
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
|
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Bad chunking");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -919,6 +916,18 @@ public class HttpParser
|
||||||
_field=null;
|
_field=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long convertContentLength(String valueString)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Long.parseLong(valueString);
|
||||||
|
}
|
||||||
|
catch(NumberFormatException e)
|
||||||
|
{
|
||||||
|
LOG.ignore(e);
|
||||||
|
throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Invalid Content-Length Value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------------- */
|
||||||
/*
|
/*
|
||||||
|
@ -970,12 +979,12 @@ public class HttpParser
|
||||||
setState(State.HEADER_VALUE);
|
setState(State.HEADER_VALUE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case HttpTokens.LINE_FEED:
|
case HttpTokens.LINE_FEED:
|
||||||
{
|
{
|
||||||
// process previous header
|
// process previous header
|
||||||
parsedHeader();
|
parsedHeader();
|
||||||
|
|
||||||
_contentPosition=0;
|
_contentPosition=0;
|
||||||
|
|
||||||
// End of headers!
|
// End of headers!
|
||||||
|
@ -1039,7 +1048,7 @@ public class HttpParser
|
||||||
|
|
||||||
// process previous header
|
// process previous header
|
||||||
parsedHeader();
|
parsedHeader();
|
||||||
|
|
||||||
// handle new header
|
// handle new header
|
||||||
if (buffer.hasRemaining())
|
if (buffer.hasRemaining())
|
||||||
{
|
{
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -415,32 +415,35 @@ public class LdapLoginModule extends AbstractLoginModule
|
||||||
return isAuthenticated();
|
return isAuthenticated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean authed = false;
|
||||||
|
|
||||||
if (_forceBindingLogin)
|
if (_forceBindingLogin)
|
||||||
{
|
{
|
||||||
return bindingLogin(webUserName, webCredential);
|
authed = bindingLogin(webUserName, webCredential);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This sets read and the credential
|
|
||||||
UserInfo userInfo = getUserInfo(webUserName);
|
|
||||||
|
|
||||||
if (userInfo == null)
|
|
||||||
{
|
|
||||||
setAuthenticated(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentUser(new JAASUserInfo(userInfo));
|
|
||||||
|
|
||||||
boolean authed = false;
|
|
||||||
if (webCredential instanceof String)
|
|
||||||
authed = credentialLogin(Credential.getCredential((String) webCredential));
|
|
||||||
else
|
else
|
||||||
authed = credentialLogin(webCredential);
|
{
|
||||||
|
// This sets read and the credential
|
||||||
|
UserInfo userInfo = getUserInfo(webUserName);
|
||||||
|
|
||||||
|
if (userInfo == null)
|
||||||
|
{
|
||||||
|
setAuthenticated(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentUser(new JAASUserInfo(userInfo));
|
||||||
|
|
||||||
|
if (webCredential instanceof String)
|
||||||
|
authed = credentialLogin(Credential.getCredential((String) webCredential));
|
||||||
|
else
|
||||||
|
authed = credentialLogin(webCredential);
|
||||||
|
}
|
||||||
|
|
||||||
//only fetch roles if authenticated
|
//only fetch roles if authenticated
|
||||||
if (authed)
|
if (authed)
|
||||||
getCurrentUser().fetchRoles();
|
getCurrentUser().fetchRoles();
|
||||||
|
|
||||||
return authed;
|
return authed;
|
||||||
}
|
}
|
||||||
catch (UnsupportedCallbackException e)
|
catch (UnsupportedCallbackException e)
|
||||||
|
|
|
@ -24,12 +24,14 @@ import java.io.DataOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -98,11 +100,10 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
File file = null;
|
File file = null;
|
||||||
if (_storeDir != null)
|
if (_storeDir != null)
|
||||||
{
|
{
|
||||||
file = new File(_storeDir, getFileName(id));
|
file = getFile(_storeDir, id);
|
||||||
if (file.exists() && file.getParentFile().equals(_storeDir))
|
if (file != null && file.exists() && file.getParentFile().equals(_storeDir))
|
||||||
{
|
{
|
||||||
file.delete();
|
return file.delete();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,12 +115,55 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int)
|
* @see org.eclipse.jetty.server.session.SessionDataStore#getExpired(Set, int)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<String> doGetExpired(Set<String> candidates, int expiryTimeoutSec)
|
public Set<String> doGetExpired(final Set<String> candidates, final int expiryTimeoutSec)
|
||||||
{
|
{
|
||||||
//we don't want to open up each file and check, so just leave it up to the SessionStore
|
//we don't want to open up each file and check, so just leave it up to the SessionStore
|
||||||
//TODO as the session manager is likely to be a lazy loader, if a session is never requested, its
|
//TODO as the session manager is likely to be a lazy loader, if a session is never requested, its
|
||||||
//file will stay forever after a restart
|
//file will stay forever after a restart
|
||||||
return candidates;
|
final long now = System.currentTimeMillis();
|
||||||
|
HashSet<String> expired = new HashSet<String>();
|
||||||
|
|
||||||
|
File[] files = _storeDir.listFiles(new FilenameFilter()
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name)
|
||||||
|
{
|
||||||
|
if (dir != _storeDir)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
String s = name.substring(0, name.indexOf('_'));
|
||||||
|
long expiry = (s==null?0:Long.parseLong(s));
|
||||||
|
|
||||||
|
if (expiry > 0 && expiry < now)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (files != null)
|
||||||
|
{
|
||||||
|
for (File f:files)
|
||||||
|
{
|
||||||
|
expired.add(getIdFromFile(f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//check candidates that were not found to be expired, perhaps they no
|
||||||
|
//longer exist and they should be expired
|
||||||
|
for (String c:candidates)
|
||||||
|
{
|
||||||
|
if (!expired.contains(c))
|
||||||
|
{
|
||||||
|
//check if the file exists
|
||||||
|
File f = getFile(_storeDir, c);
|
||||||
|
if (f == null || !f.exists())
|
||||||
|
expired.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,9 +180,9 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
{
|
{
|
||||||
public void run ()
|
public void run ()
|
||||||
{
|
{
|
||||||
File file = new File(_storeDir,getFileName(id));
|
File file = getFile(_storeDir,id);
|
||||||
|
|
||||||
if (!file.exists())
|
if (file == null || !file.exists())
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("No file: {}",file);
|
LOG.debug("No file: {}",file);
|
||||||
|
@ -187,9 +231,13 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
File file = null;
|
File file = null;
|
||||||
if (_storeDir != null)
|
if (_storeDir != null)
|
||||||
{
|
{
|
||||||
file = new File(_storeDir, getFileName(id));
|
//remove any existing file for the session
|
||||||
if (file.exists())
|
file = getFile(_storeDir, id);
|
||||||
|
if (file != null && file.exists())
|
||||||
file.delete();
|
file.delete();
|
||||||
|
|
||||||
|
//make a fresh file using the latest session expiry
|
||||||
|
file = new File(_storeDir, getFileNameWithExpiry(data));
|
||||||
|
|
||||||
try(FileOutputStream fos = new FileOutputStream(file,false))
|
try(FileOutputStream fos = new FileOutputStream(file,false))
|
||||||
{
|
{
|
||||||
|
@ -264,7 +312,50 @@ public class FileSessionDataStore extends AbstractSessionDataStore
|
||||||
{
|
{
|
||||||
return _context.getCanonicalContextPath()+"_"+_context.getVhost()+"_"+id;
|
return _context.getCanonicalContextPath()+"_"+_context.getVhost()+"_"+id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getFileNameWithExpiry (SessionData data)
|
||||||
|
{
|
||||||
|
return ""+data.getExpiry()+"_"+getFileName(data.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getIdFromFile (File file)
|
||||||
|
{
|
||||||
|
if (file == null)
|
||||||
|
return null;
|
||||||
|
String name = file.getName();
|
||||||
|
|
||||||
|
return name.substring(name.lastIndexOf('_')+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a File for the session id for the current context.
|
||||||
|
*
|
||||||
|
* @param storeDir
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private File getFile (final File storeDir, final String id)
|
||||||
|
{
|
||||||
|
File[] files = storeDir.listFiles (new FilenameFilter() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see java.io.FilenameFilter#accept(java.io.File, java.lang.String)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name)
|
||||||
|
{
|
||||||
|
if (dir != storeDir)
|
||||||
|
return false;
|
||||||
|
return (name.contains(getFileName(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (files == null || files.length < 1)
|
||||||
|
return null;
|
||||||
|
return files[0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param is inputstream containing session data
|
* @param is inputstream containing session data
|
||||||
|
|
|
@ -408,6 +408,8 @@ public class Session implements SessionManager.SessionIf
|
||||||
_sessionData.setDirty(true);
|
_sessionData.setDirty(true);
|
||||||
if (secs <= 0)
|
if (secs <= 0)
|
||||||
LOG.warn("Session {} is now immortal (maxInactiveInterval={})", _sessionData.getId(), secs);
|
LOG.warn("Session {} is now immortal (maxInactiveInterval={})", _sessionData.getId(), secs);
|
||||||
|
else if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Session {} maxInactiveInterval={}", _sessionData.getId(), secs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -663,9 +663,10 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
|
||||||
public void setMaxInactiveInterval(int seconds)
|
public void setMaxInactiveInterval(int seconds)
|
||||||
{
|
{
|
||||||
_dftMaxIdleSecs=seconds;
|
_dftMaxIdleSecs=seconds;
|
||||||
if (_dftMaxIdleSecs < 0)
|
if (_dftMaxIdleSecs <= 0)
|
||||||
LOG.warn("Sessions created by this manager are immortal (default maxInactiveInterval={})"+_dftMaxIdleSecs);
|
LOG.warn("Sessions created by this manager are immortal (default maxInactiveInterval={})"+_dftMaxIdleSecs);
|
||||||
|
else if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("SessionManager default maxInactiveInterval={}", _dftMaxIdleSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
package org.eclipse.jetty.server.session;
|
package org.eclipse.jetty.server.session;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
|
@ -153,9 +154,22 @@ public class FileSessionManagerTest
|
||||||
manager.setMaxInactiveInterval(30); // change max inactive interval for *new* sessions
|
manager.setMaxInactiveInterval(30); // change max inactive interval for *new* sessions
|
||||||
manager.stop();
|
manager.stop();
|
||||||
|
|
||||||
String expectedFilename = "_0.0.0.0_"+session.getId();
|
final String expectedFilename = "_0.0.0.0_"+session.getId();
|
||||||
Assert.assertTrue("File should exist!", new File(testDir, expectedFilename).exists());
|
|
||||||
|
File[] files = testDir.listFiles(new FilenameFilter(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name)
|
||||||
|
{
|
||||||
|
return name.contains(expectedFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
Assert.assertNotNull(files);
|
||||||
|
Assert.assertEquals(1, files.length);
|
||||||
|
Assert.assertTrue("File should exist!", files[0].exists());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
manager.start();
|
manager.start();
|
||||||
|
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -269,7 +269,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
<artifactId>jetty-version-maven-plugin</artifactId>
|
<artifactId>jetty-version-maven-plugin</artifactId>
|
||||||
<version>1.0.10</version>
|
<version>1.1</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
|
|
@ -215,6 +215,57 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangeNewSessionTimeout () throws Exception
|
||||||
|
{
|
||||||
|
String contextPath = "";
|
||||||
|
String servletMapping = "/server";
|
||||||
|
int inactivePeriod = 10;
|
||||||
|
int scavengePeriod = 1;
|
||||||
|
int inspectPeriod = 1;
|
||||||
|
int idlePassivatePeriod = 0;
|
||||||
|
AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod,inspectPeriod, idlePassivatePeriod);
|
||||||
|
ImmediateChangeTimeoutServlet servlet = new ImmediateChangeTimeoutServlet();
|
||||||
|
ServletHolder holder = new ServletHolder(servlet);
|
||||||
|
ServletContextHandler context = server1.addContext(contextPath);
|
||||||
|
context.addServlet(holder, servletMapping);
|
||||||
|
TestHttpSessionListener listener = new TestHttpSessionListener();
|
||||||
|
|
||||||
|
context.getSessionHandler().addEventListener(listener);
|
||||||
|
|
||||||
|
server1.start();
|
||||||
|
int port1 = server1.getPort();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpClient client = new HttpClient();
|
||||||
|
client.start();
|
||||||
|
String url = "http://localhost:" + port1 + contextPath + servletMapping;
|
||||||
|
|
||||||
|
inactivePeriod = 5; //change from the sessionmanager configured default
|
||||||
|
|
||||||
|
//make a request to set up a session on the server and change its inactive setting straight away
|
||||||
|
ContentResponse response1 = client.GET(url + "?action=init&val="+inactivePeriod);
|
||||||
|
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
|
||||||
|
String sessionCookie = response1.getHeaders().get("Set-Cookie");
|
||||||
|
assertTrue(sessionCookie != null);
|
||||||
|
// Mangle the cookie, replacing Path with $Path, etc.
|
||||||
|
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
|
||||||
|
|
||||||
|
String sessionId = AbstractTestServer.extractSessionId(sessionCookie);
|
||||||
|
|
||||||
|
DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
|
||||||
|
verifySessionCreated(listener,sessionId);
|
||||||
|
//verify that the session timeout is the new value and not the default
|
||||||
|
verifySessionTimeout(sessions, sessionId, inactivePeriod);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
server1.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void verifySessionTimeout (DBCollection sessions, String id, int sec) throws Exception
|
public void verifySessionTimeout (DBCollection sessions, String id, int sec) throws Exception
|
||||||
{
|
{
|
||||||
long val;
|
long val;
|
||||||
|
@ -299,5 +350,31 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ImmediateChangeTimeoutServlet extends HttpServlet
|
||||||
|
{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
String action = request.getParameter("action");
|
||||||
|
if ("init".equals(action))
|
||||||
|
{
|
||||||
|
HttpSession session = request.getSession(true);
|
||||||
|
assertNotNull(session);
|
||||||
|
String tmp = request.getParameter("val");
|
||||||
|
int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
|
||||||
|
session.setMaxInactiveInterval(val);
|
||||||
|
}
|
||||||
|
else if ("change".equals(action))
|
||||||
|
{
|
||||||
|
String tmp = request.getParameter("val");
|
||||||
|
int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
|
||||||
|
HttpSession session = request.getSession(false);
|
||||||
|
assertNotNull(session);
|
||||||
|
session.setMaxInactiveInterval(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue