Merge branch 'jetty-9.4.x' into jetty-10.0.x
Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
commit
254dc88743
|
@ -47,7 +47,6 @@ import org.eclipse.jetty.server.SslConnectionFactory;
|
|||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
@ -209,9 +208,7 @@ public class LikeJettyXml
|
|||
requestLog.setExtended(true);
|
||||
requestLog.setLogCookies(false);
|
||||
requestLog.setLogTimeZone("GMT");
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
handlers.addHandler(requestLogHandler);
|
||||
server.setRequestLog(requestLog);
|
||||
|
||||
|
||||
// === jetty-lowresources.xml ===
|
||||
|
|
|
@ -35,7 +35,6 @@ import org.eclipse.jetty.server.handler.DefaultHandler;
|
|||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerList;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.gzip.GzipHandler;
|
||||
import org.eclipse.jetty.util.ajax.JSON;
|
||||
|
||||
|
@ -115,13 +114,12 @@ public class ManyHandlers
|
|||
HandlerWrapper wrapper = new WelcomeWrapHandler();
|
||||
Handler hello = new HelloHandler();
|
||||
Handler dft = new DefaultHandler();
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
|
||||
// configure request logging
|
||||
File requestLogFile = File.createTempFile("demo", "log");
|
||||
NCSARequestLog ncsaLog = new NCSARequestLog(
|
||||
requestLogFile.getAbsolutePath());
|
||||
requestLog.setRequestLog(ncsaLog);
|
||||
server.setRequestLog(ncsaLog);
|
||||
|
||||
// create the handler collections
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
|
@ -129,20 +127,8 @@ public class ManyHandlers
|
|||
|
||||
// link them all together
|
||||
wrapper.setHandler(hello);
|
||||
list.setHandlers(new Handler[] { param, new GzipHandler(), dft });
|
||||
handlers.setHandlers(new Handler[] { list, requestLog });
|
||||
|
||||
// Handler tree looks like the following
|
||||
// <pre>
|
||||
// Server
|
||||
// + HandlerCollection
|
||||
// . + HandlerList
|
||||
// . | + param (ParamHandler)
|
||||
// . | + wrapper (WelcomeWrapHandler)
|
||||
// . | | \ hello (HelloHandler)
|
||||
// . | \ dft (DefaultHandler)
|
||||
// . \ requestLog (RequestLogHandler)
|
||||
// </pre>
|
||||
list.setHandlers(new Handler[] { param, new GzipHandler() });
|
||||
handlers.setHandlers(new Handler[] { list, dft });
|
||||
|
||||
server.setHandler(handlers);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<instructions>
|
||||
<Import-Package>org.objectweb.asm;version="[5.0,7)",*</Import-Package>
|
||||
<Import-Package>org.objectweb.asm;version="5",*</Import-Package>
|
||||
<Require-Capability>osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional</Require-Capability>
|
||||
</instructions>
|
||||
</configuration>
|
||||
|
|
|
@ -40,7 +40,6 @@ import org.eclipse.jetty.server.handler.ContextHandler;
|
|||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.util.Scanner;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.xml.XmlConfiguration;
|
||||
|
@ -407,9 +406,8 @@ public class ServerProxyImpl implements ServerProxy
|
|||
*/
|
||||
private void configureHandlers()
|
||||
{
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
if (requestLog != null)
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
server.setRequestLog(requestLog);
|
||||
|
||||
contexts = (ContextHandlerCollection) server
|
||||
.getChildHandlerByClass(ContextHandlerCollection.class);
|
||||
|
@ -422,8 +420,7 @@ public class ServerProxyImpl implements ServerProxy
|
|||
{
|
||||
handlers = new HandlerCollection();
|
||||
server.setHandler(handlers);
|
||||
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(),
|
||||
requestLogHandler });
|
||||
handlers.setHandlers(new Handler[] { contexts, new DefaultHandler() });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -550,14 +550,14 @@ public abstract class HttpReceiver
|
|||
// respect to concurrency between request and response.
|
||||
Result result = exchange.terminateResponse();
|
||||
terminateResponse(exchange, result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Concurrent failure: response termination skipped, performed by helpers");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateResponseState(ResponseState from, ResponseState to)
|
||||
|
|
|
@ -76,7 +76,7 @@ public class HttpRequest implements Request
|
|||
private String query;
|
||||
private String method = HttpMethod.GET.asString();
|
||||
private HttpVersion version = HttpVersion.HTTP_1_1;
|
||||
private long idleTimeout;
|
||||
private long idleTimeout = -1;
|
||||
private long timeout;
|
||||
private long timeoutAt;
|
||||
private ContentProvider content;
|
||||
|
@ -99,7 +99,6 @@ public class HttpRequest implements Request
|
|||
extractParams(query);
|
||||
|
||||
followRedirects(client.isFollowRedirects());
|
||||
idleTimeout = client.getIdleTimeout();
|
||||
HttpField acceptEncodingField = client.getAcceptEncodingField();
|
||||
if (acceptEncodingField != null)
|
||||
headers.put(acceptEncodingField);
|
||||
|
|
|
@ -579,14 +579,14 @@ public abstract class HttpSender implements AsyncContentProvider.Listener
|
|||
// respect to concurrency between request and response.
|
||||
Result result = exchange.terminateRequest();
|
||||
terminateRequest(exchange, failure, result);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Concurrent failure: request termination skipped, performed by helpers");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean updateRequestState(RequestState from, RequestState to)
|
||||
|
|
|
@ -247,7 +247,9 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
|
|||
// Save the old idle timeout to restore it.
|
||||
EndPoint endPoint = getEndPoint();
|
||||
idleTimeout = endPoint.getIdleTimeout();
|
||||
endPoint.setIdleTimeout(request.getIdleTimeout());
|
||||
long requestIdleTimeout = request.getIdleTimeout();
|
||||
if (requestIdleTimeout >= 0)
|
||||
endPoint.setIdleTimeout(requestIdleTimeout);
|
||||
|
||||
// One channel per connection, just delegate the send.
|
||||
return send(channel, exchange);
|
||||
|
|
|
@ -110,7 +110,7 @@ public class NeedWantClientAuthTest
|
|||
startClient(clientSSL);
|
||||
|
||||
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.timeout(10, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
|
@ -149,11 +149,11 @@ public class NeedWantClientAuthTest
|
|||
startClient(clientSSL);
|
||||
|
||||
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.timeout(10, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertTrue(handshakeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(handshakeLatch.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -192,7 +192,7 @@ public class NeedWantClientAuthTest
|
|||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
client.newRequest("https://localhost:" + connector.getLocalPort())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.timeout(10, TimeUnit.SECONDS)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.isFailed())
|
||||
|
@ -203,8 +203,8 @@ public class NeedWantClientAuthTest
|
|||
}
|
||||
});
|
||||
|
||||
assertTrue(handshakeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(handshakeLatch.await(10, TimeUnit.SECONDS));
|
||||
assertTrue(latch.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -240,10 +240,10 @@ public class NeedWantClientAuthTest
|
|||
startClient(clientSSL);
|
||||
|
||||
ContentResponse response = client.newRequest("https://localhost:" + connector.getLocalPort())
|
||||
.timeout(5, TimeUnit.SECONDS)
|
||||
.timeout(10, TimeUnit.SECONDS)
|
||||
.send();
|
||||
|
||||
assertEquals(HttpStatus.OK_200, response.getStatus());
|
||||
assertTrue(handshakeLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(handshakeLatch.await(10, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ public class HttpChannelOverFCGI extends HttpChannel
|
|||
{
|
||||
super(connection.getHttpDestination().getHttpClient().getScheduler());
|
||||
this.connection = connection;
|
||||
setIdleTimeout(idleTimeout);
|
||||
setIdleTimeout(idleTimeout >= 0 ? idleTimeout : connection.getEndPoint().getIdleTimeout());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -46,7 +46,6 @@ public abstract class CookieCutter
|
|||
{
|
||||
// Parse the header
|
||||
String name = null;
|
||||
String value = null;
|
||||
|
||||
String cookieName = null;
|
||||
String cookieValue = null;
|
||||
|
@ -61,11 +60,11 @@ public abstract class CookieCutter
|
|||
boolean escaped=false;
|
||||
int tokenstart=-1;
|
||||
int tokenend=-1;
|
||||
for (int i = 0, length = hdr.length(), last=length-1; i < length; i++)
|
||||
for (int i = 0, length = hdr.length(); i <= length; i++)
|
||||
{
|
||||
char c = hdr.charAt(i);
|
||||
char c = i==length?0:hdr.charAt(i);
|
||||
|
||||
// System.err.printf("i=%d c=%s v=%b q=%b e=%b u=%s s=%d e=%d%n" ,i,""+c,invalue,inQuoted,escaped,unquoted,tokenstart,tokenend);
|
||||
// System.err.printf("i=%d/%d c=%s v=%b q=%b/%b e=%b u=%s s=%d e=%d \t%s=%s%n" ,i,length,c==0?"|":(""+c),invalue,inQuoted,quoted,escaped,unquoted,tokenstart,tokenend,name,value);
|
||||
|
||||
// Handle quoted values for name or value
|
||||
if (inQuoted)
|
||||
|
@ -73,52 +72,39 @@ public abstract class CookieCutter
|
|||
if (escaped)
|
||||
{
|
||||
escaped=false;
|
||||
unquoted.append(c);
|
||||
if (c>0)
|
||||
unquoted.append(c);
|
||||
else
|
||||
{
|
||||
unquoted.setLength(0);
|
||||
inQuoted = false;
|
||||
i--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
inQuoted=false;
|
||||
if (i==last)
|
||||
{
|
||||
value = unquoted.toString();
|
||||
unquoted.setLength(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
quoted=true;
|
||||
tokenstart=i;
|
||||
tokenend=-1;
|
||||
}
|
||||
inQuoted = false;
|
||||
quoted = true;
|
||||
tokenstart = i;
|
||||
tokenend = -1;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (i==last)
|
||||
{
|
||||
unquoted.setLength(0);
|
||||
inQuoted = false;
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
escaped=true;
|
||||
}
|
||||
escaped = true;
|
||||
continue;
|
||||
|
||||
case 0:
|
||||
// unterminated quote, let's ignore quotes
|
||||
unquoted.setLength(0);
|
||||
inQuoted = false;
|
||||
i--;
|
||||
continue;
|
||||
|
||||
default:
|
||||
if (i==last)
|
||||
{
|
||||
// unterminated quote, let's ignore quotes
|
||||
unquoted.setLength(0);
|
||||
inQuoted = false;
|
||||
i--;
|
||||
}
|
||||
else
|
||||
{
|
||||
unquoted.append(c);
|
||||
}
|
||||
unquoted.append(c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -134,21 +120,90 @@ public abstract class CookieCutter
|
|||
case '\t':
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (_compliance!=CookieCompliance.RFC2965)
|
||||
{
|
||||
if (quoted)
|
||||
{
|
||||
// must have been a bad internal quote. let's fix as best we can
|
||||
unquoted.append(hdr,tokenstart,i--);
|
||||
inQuoted = true;
|
||||
quoted = false;
|
||||
continue;
|
||||
}
|
||||
if (tokenstart<0)
|
||||
tokenstart = i;
|
||||
tokenend=i;
|
||||
continue;
|
||||
}
|
||||
// fall through
|
||||
case 0:
|
||||
case ';':
|
||||
{
|
||||
String value;
|
||||
|
||||
if (quoted)
|
||||
{
|
||||
value = unquoted.toString();
|
||||
unquoted.setLength(0);
|
||||
quoted = false;
|
||||
}
|
||||
else if(tokenstart>=0 && tokenend>=0)
|
||||
value = hdr.substring(tokenstart, tokenend+1);
|
||||
else if(tokenstart>=0)
|
||||
value = tokenend>=tokenstart?hdr.substring(tokenstart, tokenend+1):hdr.substring(tokenstart);
|
||||
else
|
||||
value = "";
|
||||
|
||||
try
|
||||
{
|
||||
if (name.startsWith("$"))
|
||||
{
|
||||
if (_compliance==CookieCompliance.RFC2965)
|
||||
{
|
||||
String lowercaseName = name.toLowerCase(Locale.ENGLISH);
|
||||
switch(lowercaseName)
|
||||
{
|
||||
case "$path":
|
||||
cookiePath = value;
|
||||
break;
|
||||
case "$domain":
|
||||
cookieDomain = value;
|
||||
break;
|
||||
case "$port":
|
||||
cookieComment = "$port="+value;
|
||||
break;
|
||||
case "$version":
|
||||
cookieVersion = Integer.parseInt(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a new cookie, so add the completed last cookie if we have one
|
||||
if (cookieName!=null)
|
||||
{
|
||||
addCookie(cookieName, cookieValue, cookieDomain, cookiePath, cookieVersion, cookieComment);
|
||||
cookieDomain = null;
|
||||
cookiePath = null;
|
||||
cookieComment = null;
|
||||
}
|
||||
cookieName = name;
|
||||
cookieValue = value;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
name = null;
|
||||
tokenstart = -1;
|
||||
invalue=false;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '"':
|
||||
if (tokenstart<0)
|
||||
|
@ -165,20 +220,14 @@ public abstract class CookieCutter
|
|||
if (quoted)
|
||||
{
|
||||
// must have been a bad internal quote. let's fix as best we can
|
||||
unquoted.append(hdr.substring(tokenstart,i));
|
||||
unquoted.append(hdr,tokenstart,i--);
|
||||
inQuoted = true;
|
||||
quoted = false;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (tokenstart<0)
|
||||
tokenstart=i;
|
||||
tokenstart = i;
|
||||
tokenend=i;
|
||||
if (i==last)
|
||||
{
|
||||
value = hdr.substring(tokenstart, tokenend+1);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -191,21 +240,6 @@ public abstract class CookieCutter
|
|||
case '\t':
|
||||
continue;
|
||||
|
||||
case ';':
|
||||
if (quoted)
|
||||
{
|
||||
name = unquoted.toString();
|
||||
unquoted.setLength(0);
|
||||
quoted = false;
|
||||
}
|
||||
else if(tokenstart>=0 && tokenend>=0)
|
||||
{
|
||||
name = hdr.substring(tokenstart, tokenend+1);
|
||||
}
|
||||
|
||||
tokenstart = -1;
|
||||
break;
|
||||
|
||||
case '=':
|
||||
if (quoted)
|
||||
{
|
||||
|
@ -213,100 +247,29 @@ public abstract class CookieCutter
|
|||
unquoted.setLength(0);
|
||||
quoted = false;
|
||||
}
|
||||
else if(tokenstart>=0 && tokenend>=0)
|
||||
{
|
||||
name = hdr.substring(tokenstart, tokenend+1);
|
||||
}
|
||||
else if(tokenstart>=0)
|
||||
name = tokenend>=tokenstart?hdr.substring(tokenstart, tokenend+1):hdr.substring(tokenstart);
|
||||
|
||||
tokenstart = -1;
|
||||
invalue=true;
|
||||
invalue = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (quoted)
|
||||
{
|
||||
// must have been a bad internal quote. let's fix as best we can
|
||||
unquoted.append(hdr.substring(tokenstart,i));
|
||||
unquoted.append(hdr,tokenstart,i--);
|
||||
inQuoted = true;
|
||||
quoted = false;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (tokenstart<0)
|
||||
tokenstart=i;
|
||||
tokenend=i;
|
||||
if (i==last)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (invalue && i==last && value==null)
|
||||
{
|
||||
if (quoted)
|
||||
{
|
||||
value = unquoted.toString();
|
||||
unquoted.setLength(0);
|
||||
quoted = false;
|
||||
}
|
||||
else if(tokenstart>=0 && tokenend>=0)
|
||||
{
|
||||
value = hdr.substring(tokenstart, tokenend+1);
|
||||
}
|
||||
else
|
||||
value = "";
|
||||
}
|
||||
|
||||
// If after processing the current character we have a value and a name, then it is a cookie
|
||||
if (name!=null && value!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (name.startsWith("$"))
|
||||
{
|
||||
String lowercaseName = name.toLowerCase(Locale.ENGLISH);
|
||||
if (_compliance==CookieCompliance.RFC6265)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
else if ("$path".equals(lowercaseName))
|
||||
{
|
||||
cookiePath = value;
|
||||
}
|
||||
else if ("$domain".equals(lowercaseName))
|
||||
{
|
||||
cookieDomain = value;
|
||||
}
|
||||
else if ("$port".equals(lowercaseName))
|
||||
{
|
||||
cookieComment = (cookieComment==null?"$port=":", $port=")+value;
|
||||
}
|
||||
else if ("$version".equals(lowercaseName))
|
||||
{
|
||||
cookieVersion = Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cookieName!=null)
|
||||
{
|
||||
addCookie(cookieName, cookieValue, cookieDomain, cookiePath, cookieVersion, cookieComment);
|
||||
cookieDomain = null;
|
||||
cookiePath = null;
|
||||
cookieComment = null;
|
||||
}
|
||||
cookieName = name;
|
||||
cookieValue = value;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
}
|
||||
|
||||
name = null;
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (cookieName!=null)
|
||||
|
|
|
@ -145,7 +145,12 @@ public class CookieCutterLenientTest
|
|||
Arguments.of("GAPS=1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-", "GAPS", "1:A1aaaAaAA1aaAAAaa1a11a:aAaaAa-aaA1-"),
|
||||
|
||||
// Strong abuse of cookie spec (lots of tspecials)
|
||||
Arguments.of("$Version=0; rToken=F_TOKEN''!--\"</a>=&{()}", "rToken", "F_TOKEN''!--\"</a>=&{()}")
|
||||
Arguments.of("$Version=0; rToken=F_TOKEN''!--\"</a>=&{()}", "rToken", "F_TOKEN''!--\"</a>=&{()}"),
|
||||
|
||||
// Commas that were not commas
|
||||
Arguments.of("name=foo,bar","name","foo,bar"),
|
||||
Arguments.of("name=foo , bar","name","foo , bar"),
|
||||
Arguments.of("name=foo , bar, bob","name","foo , bar, bob")
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -140,17 +140,22 @@ public class CookieCutterTest
|
|||
* Example from RFC2965
|
||||
*/
|
||||
@Test
|
||||
@Disabled("comma separation no longer supported by new RFC6265")
|
||||
public void testRFC2965_CookieSpoofingExample()
|
||||
{
|
||||
String rawCookie = "$Version=\"1\"; session_id=\"1234\", " +
|
||||
"$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\"";
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
|
||||
|
||||
|
||||
|
||||
Cookie cookies[] = parseCookieHeaders(CookieCompliance.RFC2965,rawCookie);
|
||||
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "session_id", "1234", 1, null);
|
||||
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 1, null);
|
||||
|
||||
cookies = parseCookieHeaders(CookieCompliance.RFC6265,rawCookie);
|
||||
assertThat("Cookies.length", cookies.length, is(2));
|
||||
assertCookie("Cookies[0]", cookies[0], "session_id", "1234\", $Version=\"1", 0, null);
|
||||
assertCookie("Cookies[1]", cookies[1], "session_id", "1111", 0, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -562,7 +562,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
IStream stream = createLocalStream(streamId);
|
||||
stream.setListener(listener);
|
||||
|
||||
ControlEntry entry = new ControlEntry(frame, stream, new PromiseCallback<>(promise, stream));
|
||||
ControlEntry entry = new ControlEntry(frame, stream, new StreamPromiseCallback(promise, stream));
|
||||
queued = flusher.append(entry);
|
||||
}
|
||||
// Iterate outside the synchronized block.
|
||||
|
@ -606,7 +606,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
IStream pushStream = createLocalStream(streamId);
|
||||
pushStream.setListener(listener);
|
||||
|
||||
ControlEntry entry = new ControlEntry(frame, pushStream, new PromiseCallback<>(promise, pushStream));
|
||||
ControlEntry entry = new ControlEntry(frame, pushStream, new StreamPromiseCallback(promise, pushStream));
|
||||
queued = flusher.append(entry);
|
||||
}
|
||||
// Iterate outside the synchronized block.
|
||||
|
@ -764,7 +764,8 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
int localCount = localStreamCount.get();
|
||||
int maxCount = getMaxLocalStreams();
|
||||
if (maxCount >= 0 && localCount >= maxCount)
|
||||
throw new IllegalStateException("Max local stream count " + maxCount + " exceeded");
|
||||
// TODO: remove the dump() in the exception message.
|
||||
throw new IllegalStateException("Max local stream count " + maxCount + " exceeded" + System.lineSeparator() + dump());
|
||||
if (localStreamCount.compareAndSet(localCount, localCount + 1))
|
||||
break;
|
||||
}
|
||||
|
@ -780,6 +781,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
else
|
||||
{
|
||||
localStreamCount.decrementAndGet();
|
||||
throw new IllegalStateException("Duplicate stream " + streamId);
|
||||
}
|
||||
}
|
||||
|
@ -816,6 +818,7 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
else
|
||||
{
|
||||
remoteStreamCount.addAndGetHi(-1);
|
||||
onConnectionFailure(ErrorCode.PROTOCOL_ERROR.code, "duplicate_stream");
|
||||
return null;
|
||||
}
|
||||
|
@ -1461,21 +1464,21 @@ public abstract class HTTP2Session extends ContainerLifeCycle implements ISessio
|
|||
}
|
||||
}
|
||||
|
||||
private static class PromiseCallback<C> implements Callback
|
||||
private static class StreamPromiseCallback implements Callback
|
||||
{
|
||||
private final Promise<C> promise;
|
||||
private final C value;
|
||||
private final Promise<Stream> promise;
|
||||
private final IStream stream;
|
||||
|
||||
private PromiseCallback(Promise<C> promise, C value)
|
||||
private StreamPromiseCallback(Promise<Stream> promise, IStream stream)
|
||||
{
|
||||
this.promise = promise;
|
||||
this.value = value;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
promise.succeeded(value);
|
||||
promise.succeeded(stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -138,6 +138,7 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
{
|
||||
if (writing.compareAndSet(null, callback))
|
||||
return true;
|
||||
close();
|
||||
callback.failed(new WritePendingException());
|
||||
return false;
|
||||
}
|
||||
|
@ -275,8 +276,6 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
|
||||
private void onHeaders(HeadersFrame frame, Callback callback)
|
||||
{
|
||||
if (updateClose(frame.isEndStream(), CloseState.Event.RECEIVED))
|
||||
session.removeStream(this);
|
||||
MetaData metaData = frame.getMetaData();
|
||||
if (metaData.isRequest() || metaData.isResponse())
|
||||
{
|
||||
|
@ -286,6 +285,10 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
length = fields.getLongField(HttpHeader.CONTENT_LENGTH.asString());
|
||||
dataLength = length >= 0 ? length : Long.MIN_VALUE;
|
||||
}
|
||||
|
||||
if (updateClose(frame.isEndStream(), CloseState.Event.RECEIVED))
|
||||
session.removeStream(this);
|
||||
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
|
@ -507,6 +510,13 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
super.onClose();
|
||||
notifyClosed(this);
|
||||
}
|
||||
|
||||
private void updateStreamCount(int deltaStream, int deltaClosing)
|
||||
{
|
||||
((HTTP2Session)session).updateStreamCount(isLocal(), deltaStream, deltaClosing);
|
||||
|
@ -612,6 +622,21 @@ public class HTTP2Stream extends IdleTimeout implements IStream, Callback, Dumpa
|
|||
}
|
||||
}
|
||||
|
||||
private void notifyClosed(Stream stream)
|
||||
{
|
||||
Listener listener = this.listener;
|
||||
if (listener == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
listener.onClosed(stream);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Failure while notifying listener " + listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
|
|
|
@ -163,6 +163,13 @@ public interface Stream
|
|||
*/
|
||||
public void onData(Stream stream, DataFrame frame, Callback callback);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when a RST_STREAM frame has been received for this stream.</p>
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param frame the RST_FRAME received
|
||||
* @param callback the callback to complete when the reset has been handled
|
||||
*/
|
||||
public default void onReset(Stream stream, ResetFrame frame, Callback callback)
|
||||
{
|
||||
try
|
||||
|
@ -197,11 +204,28 @@ public interface Stream
|
|||
*/
|
||||
public boolean onIdleTimeout(Stream stream, Throwable x);
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked when the stream failed.</p>
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param error the error code
|
||||
* @param reason the error reason, or null
|
||||
* @param callback the callback to complete when the failure has been handled
|
||||
*/
|
||||
public default void onFailure(Stream stream, int error, String reason, Callback callback)
|
||||
{
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Callback method invoked after the stream has been closed.</p>
|
||||
*
|
||||
* @param stream the stream
|
||||
*/
|
||||
public default void onClosed(Stream stream)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Empty implementation of {@link Listener}</p>
|
||||
*/
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.eclipse.jetty.client.HttpReceiver;
|
|||
import org.eclipse.jetty.client.HttpSender;
|
||||
import org.eclipse.jetty.client.api.Result;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.IStream;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.ResetFrame;
|
||||
|
@ -101,6 +102,11 @@ public class HttpChannelOverHTTP2 extends HttpChannel
|
|||
connection.release(this);
|
||||
}
|
||||
|
||||
void onStreamClosed(IStream stream)
|
||||
{
|
||||
connection.onStreamClosed(stream, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exchangeTerminated(HttpExchange exchange, Result result)
|
||||
{
|
||||
|
|
|
@ -35,12 +35,17 @@ import org.eclipse.jetty.client.HttpRequest;
|
|||
import org.eclipse.jetty.client.SendFailure;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.IStream;
|
||||
import org.eclipse.jetty.http2.api.Session;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Sweeper;
|
||||
|
||||
public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.Sweepable
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HttpConnection.class);
|
||||
|
||||
private final Set<HttpChannel> activeChannels = ConcurrentHashMap.newKeySet();
|
||||
private final Queue<HttpChannelOverHTTP2> idleChannels = new ConcurrentLinkedQueue<>();
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
@ -87,16 +92,15 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
|
|||
|
||||
protected void release(HttpChannelOverHTTP2 channel)
|
||||
{
|
||||
// Only non-push channels are released.
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Released {}", channel);
|
||||
if (activeChannels.remove(channel))
|
||||
{
|
||||
channel.setStream(null);
|
||||
// Recycle only non-failed channels.
|
||||
if (channel.isFailed())
|
||||
channel.destroy();
|
||||
else
|
||||
idleChannels.offer(channel);
|
||||
getHttpDestination().release(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -104,6 +108,16 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
|
|||
}
|
||||
}
|
||||
|
||||
void onStreamClosed(IStream stream, HttpChannelOverHTTP2 channel)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} closed for {}", stream, channel);
|
||||
channel.setStream(null);
|
||||
// Only non-push channels are released.
|
||||
if (stream.isLocal())
|
||||
getHttpDestination().release(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onIdleTimeout(long idleTimeout)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.eclipse.jetty.http.HttpFields;
|
|||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.ErrorCode;
|
||||
import org.eclipse.jetty.http2.IStream;
|
||||
import org.eclipse.jetty.http2.api.Stream;
|
||||
import org.eclipse.jetty.http2.frames.DataFrame;
|
||||
import org.eclipse.jetty.http2.frames.HeadersFrame;
|
||||
|
@ -171,8 +172,10 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
|
|||
@Override
|
||||
public boolean onIdleTimeout(Stream stream, Throwable x)
|
||||
{
|
||||
responseFailure(x);
|
||||
return true;
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
return false;
|
||||
return !exchange.abort(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -182,6 +185,12 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
|
|||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(Stream stream)
|
||||
{
|
||||
getHttpChannel().onStreamClosed((IStream)stream);
|
||||
}
|
||||
|
||||
private void notifyContent(HttpExchange exchange, DataFrame frame, Callback callback)
|
||||
{
|
||||
contentNotifier.offer(new DataInfo(exchange, frame, callback));
|
||||
|
|
|
@ -67,7 +67,9 @@ public class HttpSenderOverHTTP2 extends HttpSender
|
|||
{
|
||||
channel.setStream(stream);
|
||||
((IStream)stream).setAttachment(channel);
|
||||
stream.setIdleTimeout(request.getIdleTimeout());
|
||||
long idleTimeout = request.getIdleTimeout();
|
||||
if (idleTimeout >= 0)
|
||||
stream.setIdleTimeout(idleTimeout);
|
||||
|
||||
if (content.hasContent() && !expects100Continue(request))
|
||||
{
|
||||
|
|
|
@ -18,6 +18,22 @@
|
|||
|
||||
package org.eclipse.jetty.http2.client.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.AbstractConnectionPool;
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.HttpDestination;
|
||||
|
@ -43,21 +59,6 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
@ -370,7 +371,7 @@ public class MaxConcurrentStreamsTest extends AbstractTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testTwoConcurrentStreamsFirstTimesOut() throws Exception
|
||||
public void testTwoStreamsFirstTimesOut() throws Exception
|
||||
{
|
||||
long timeout = 1000;
|
||||
start(1, new EmptyServerHandler()
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2018 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.requestlog.jmh;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.dropArguments;
|
||||
import static java.lang.invoke.MethodHandles.foldArguments;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.Threads;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.profile.GCProfiler;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
import org.openjdk.jmh.runner.options.Options;
|
||||
import org.openjdk.jmh.runner.options.OptionsBuilder;
|
||||
|
||||
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@Threads(4)
|
||||
@Warmup(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 7, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
public class RequestLogBenchmark
|
||||
{
|
||||
|
||||
public static void append(String s, StringBuilder b)
|
||||
{
|
||||
b.append(s);
|
||||
}
|
||||
|
||||
public static void logURI(StringBuilder b, String request)
|
||||
{
|
||||
b.append(request);
|
||||
}
|
||||
|
||||
public static void logLength(StringBuilder b, String request)
|
||||
{
|
||||
b.append(request.length());
|
||||
}
|
||||
|
||||
public static void logAddr(StringBuilder b, String request)
|
||||
{
|
||||
try
|
||||
{
|
||||
TypeUtil.toHex(request.hashCode(), b);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ThreadLocal<StringBuilder> buffers = new ThreadLocal<StringBuilder>()
|
||||
{
|
||||
@Override
|
||||
protected StringBuilder initialValue()
|
||||
{
|
||||
return new StringBuilder(256);
|
||||
}
|
||||
};
|
||||
MethodHandle logHandle;
|
||||
Object[] iteratedLog;
|
||||
|
||||
public RequestLogBenchmark()
|
||||
{
|
||||
try
|
||||
{
|
||||
MethodType logType = methodType(Void.TYPE, StringBuilder.class, String.class);
|
||||
|
||||
MethodHandle append = MethodHandles.lookup()
|
||||
.findStatic(RequestLogBenchmark.class, "append", methodType(Void.TYPE, String.class, StringBuilder.class));
|
||||
MethodHandle logURI = MethodHandles.lookup().findStatic(RequestLogBenchmark.class, "logURI", logType);
|
||||
MethodHandle logAddr = MethodHandles.lookup().findStatic(RequestLogBenchmark.class, "logAddr", logType);
|
||||
MethodHandle logLength = MethodHandles.lookup().findStatic(RequestLogBenchmark.class, "logLength", logType);
|
||||
|
||||
// setup iteration
|
||||
iteratedLog = new Object[]
|
||||
{
|
||||
logURI,
|
||||
" - ",
|
||||
logAddr,
|
||||
" ",
|
||||
logLength,
|
||||
"\n"
|
||||
};
|
||||
|
||||
// setup methodHandle
|
||||
logHandle = dropArguments(append.bindTo("\n"), 1, String.class);
|
||||
logHandle = foldArguments(logHandle, logLength);
|
||||
logHandle = foldArguments(logHandle, dropArguments(append.bindTo(" "), 1, String.class));
|
||||
logHandle = foldArguments(logHandle, logAddr);
|
||||
logHandle = foldArguments(logHandle, dropArguments(append.bindTo(" - "), 1, String.class));
|
||||
logHandle = foldArguments(logHandle, logURI);
|
||||
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String logFixed(String request)
|
||||
{
|
||||
StringBuilder b = buffers.get();
|
||||
logURI(b,request);
|
||||
append(" - ",b);
|
||||
logAddr(b,request);
|
||||
append(" ",b);
|
||||
logLength(b,request);
|
||||
append("\n",b);
|
||||
String l = b.toString();
|
||||
b.setLength(0);
|
||||
return l;
|
||||
}
|
||||
|
||||
public String logIterate(String request)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
StringBuilder b = buffers.get();
|
||||
for (Object o : iteratedLog)
|
||||
{
|
||||
if (o instanceof String)
|
||||
append((String)o, b);
|
||||
else if (o instanceof MethodHandle)
|
||||
((MethodHandle)o).invoke(b, request);
|
||||
}
|
||||
String l = b.toString();
|
||||
b.setLength(0);
|
||||
return l;
|
||||
}
|
||||
catch(Throwable th)
|
||||
{
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
public String logMethodHandle(String request)
|
||||
{
|
||||
try
|
||||
{
|
||||
StringBuilder b = buffers.get();
|
||||
logHandle.invoke(buffers.get(), request);
|
||||
String l = b.toString();
|
||||
b.setLength(0);
|
||||
return l;
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Benchmark
|
||||
@BenchmarkMode({ Mode.Throughput})
|
||||
public String testFixed()
|
||||
{
|
||||
return logFixed(Long.toString(ThreadLocalRandom.current().nextLong()));
|
||||
};
|
||||
|
||||
@Benchmark
|
||||
@BenchmarkMode({ Mode.Throughput})
|
||||
public String testIterate()
|
||||
{
|
||||
return logIterate(Long.toString(ThreadLocalRandom.current().nextLong()));
|
||||
};
|
||||
|
||||
@Benchmark
|
||||
@BenchmarkMode({ Mode.Throughput})
|
||||
public String testHandle()
|
||||
{
|
||||
return logMethodHandle(Long.toString(ThreadLocalRandom.current().nextLong()));
|
||||
};
|
||||
|
||||
|
||||
public static void main(String[] args) throws RunnerException
|
||||
{
|
||||
Options opt = new OptionsBuilder()
|
||||
.include(RequestLogBenchmark.class.getSimpleName())
|
||||
.warmupIterations(20)
|
||||
.measurementIterations(10)
|
||||
.addProfiler(GCProfiler.class)
|
||||
.forks(1)
|
||||
.threads(100)
|
||||
.build();
|
||||
|
||||
new Runner(opt).run();
|
||||
}
|
||||
|
||||
}
|
|
@ -33,7 +33,6 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.Configurations;
|
||||
|
@ -70,9 +69,8 @@ public class ServerSupport
|
|||
throw new IllegalArgumentException ("Server is null");
|
||||
|
||||
DefaultHandler defaultHandler = new DefaultHandler();
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
if (requestLog != null)
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
server.setRequestLog(requestLog);
|
||||
|
||||
ContextHandlerCollection contexts = findContextHandlerCollection(server);
|
||||
if (contexts == null)
|
||||
|
@ -83,7 +81,7 @@ public class ServerSupport
|
|||
{
|
||||
handlers = new HandlerCollection();
|
||||
server.setHandler(handlers);
|
||||
handlers.setHandlers(new Handler[]{contexts, defaultHandler, requestLogHandler});
|
||||
handlers.setHandlers(new Handler[]{contexts, defaultHandler});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
javax.servlet.http;version="[3.1,4.1)",
|
||||
javax.transaction;version="1.1.0";resolution:=optional,
|
||||
javax.transaction.xa;version="1.1.0";resolution:=optional,
|
||||
org.objectweb.asm;version=4;resolution:=optional,
|
||||
org.objectweb.asm;version="5";resolution:=optional,
|
||||
org.osgi.framework,
|
||||
org.osgi.service.cm;version="1.2.0",
|
||||
org.osgi.service.packageadmin,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<asm.version>6.2</asm.version>
|
||||
<osgi-version>3.6.0.v20100517</osgi-version>
|
||||
<osgi-services-version>3.2.100.v20100503</osgi-services-version>
|
||||
<equinox-http-servlet-version>1.0.0-v20070606</equinox-http-servlet-version>
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.eclipse.jetty.server.handler.ContextHandler;
|
|||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
|
@ -88,7 +87,6 @@ public class Runner
|
|||
protected URLClassLoader _classLoader;
|
||||
protected Classpath _classpath = new Classpath();
|
||||
protected ContextHandlerCollection _contexts;
|
||||
protected RequestLogHandler _logHandler;
|
||||
protected String _logFile;
|
||||
protected ArrayList<String> _configFiles;
|
||||
protected boolean _enableStats=false;
|
||||
|
@ -392,14 +390,6 @@ public class Runner
|
|||
handlers.addHandler(new DefaultHandler());
|
||||
}
|
||||
|
||||
//ensure a log handler is present
|
||||
_logHandler = (RequestLogHandler) handlers.getChildHandlerByClass(RequestLogHandler.class);
|
||||
if (_logHandler == null)
|
||||
{
|
||||
_logHandler = new RequestLogHandler();
|
||||
handlers.addHandler(_logHandler);
|
||||
}
|
||||
|
||||
|
||||
//check a connector is configured to listen on
|
||||
Connector[] connectors = _server.getConnectors();
|
||||
|
@ -509,7 +499,7 @@ public class Runner
|
|||
{
|
||||
NCSARequestLog requestLog = new NCSARequestLog(_logFile);
|
||||
requestLog.setExtended(false);
|
||||
_logHandler.setRequestLog(requestLog);
|
||||
_server.setRequestLog(requestLog);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -888,7 +888,6 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
|||
@Override
|
||||
public void write(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
_written+=BufferUtil.length(content);
|
||||
sendResponse(null,content,complete,callback);
|
||||
}
|
||||
|
||||
|
@ -1226,18 +1225,21 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
|||
private class CommitCallback extends Callback.Nested
|
||||
{
|
||||
private final ByteBuffer _content;
|
||||
private final int _length;
|
||||
private final boolean _complete;
|
||||
|
||||
private CommitCallback(Callback callback, ByteBuffer content, boolean complete)
|
||||
{
|
||||
super(callback);
|
||||
this._content = content == null ? BufferUtil.EMPTY_BUFFER : content.slice();
|
||||
this._complete = complete;
|
||||
_content = content == null ? BufferUtil.EMPTY_BUFFER : content.slice();
|
||||
_length = _content.remaining();
|
||||
_complete = complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
_written += _length;
|
||||
super.succeeded();
|
||||
notifyResponseCommit(_request);
|
||||
if (_content.hasRemaining())
|
||||
|
@ -1299,18 +1301,21 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor
|
|||
private class ContentCallback extends Callback.Nested
|
||||
{
|
||||
private final ByteBuffer _content;
|
||||
private final int _length;
|
||||
private final boolean _complete;
|
||||
|
||||
private ContentCallback(Callback callback, ByteBuffer content, boolean complete)
|
||||
{
|
||||
super(callback);
|
||||
this._content = content == null ? BufferUtil.EMPTY_BUFFER : content.slice();
|
||||
this._complete = complete;
|
||||
_content = content == null ? BufferUtil.EMPTY_BUFFER : content.slice();
|
||||
_length = _content.remaining();
|
||||
_complete = complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
_written += _length;
|
||||
super.succeeded();
|
||||
if (_content.hasRemaining())
|
||||
notifyResponseContent(_request, _content);
|
||||
|
|
|
@ -286,7 +286,12 @@ public class HttpInput extends ServletInputStream implements Runnable
|
|||
{
|
||||
long minimum_data = minRequestDataRate * TimeUnit.NANOSECONDS.toMillis(period) / TimeUnit.SECONDS.toMillis(1);
|
||||
if (_contentArrived < minimum_data)
|
||||
throw new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408,String.format("Request content data rate < %d B/s",minRequestDataRate));
|
||||
{
|
||||
BadMessageException bad = new BadMessageException(HttpStatus.REQUEST_TIMEOUT_408,
|
||||
String.format("Request content data rate < %d B/s", minRequestDataRate));
|
||||
_channelState.getHttpChannel().abort(bad);
|
||||
throw bad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.nio.channels.ReadableByteChannel;
|
|||
import java.nio.channels.WritePendingException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
@ -934,7 +935,11 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Flushed bytes min/actual {}/{}", minFlushed, _flushed);
|
||||
if (_flushed < minFlushed)
|
||||
throw new IOException(String.format("Response content data rate < %d B/s", minDataRate));
|
||||
{
|
||||
IOException ioe = new IOException(String.format("Response content data rate < %d B/s", minDataRate));
|
||||
_channel.abort(ioe);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
||||
public void recycle()
|
||||
|
|
|
@ -761,12 +761,15 @@ public class Request implements HttpServletRequest
|
|||
}
|
||||
|
||||
_cookiesExtracted = true;
|
||||
|
||||
for (String c : metadata.getFields().getValuesList(HttpHeader.COOKIE))
|
||||
|
||||
for (HttpField field : metadata.getFields())
|
||||
{
|
||||
if (_cookies == null)
|
||||
_cookies = new Cookies(getHttpChannel().getHttpConfiguration().getCookieCompliance());
|
||||
_cookies.addCookieField(c);
|
||||
if (field.getHeader()==HttpHeader.COOKIE)
|
||||
{
|
||||
if (_cookies==null)
|
||||
_cookies = new Cookies(getHttpChannel().getHttpConfiguration().getCookieCompliance());
|
||||
_cookies.addCookieField(field.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
//Javadoc for Request.getCookies() stipulates null for no cookies
|
||||
|
|
|
@ -31,11 +31,11 @@ import org.eclipse.jetty.server.Server;
|
|||
|
||||
|
||||
/**
|
||||
* RequestLogHandler.
|
||||
* This handler can be used to wrap an individual context for context logging.
|
||||
* To set a {@link RequestLog} instance for the entire {@link Server}, use
|
||||
* {@link Server#setRequestLog(RequestLog)} instead of this handler.
|
||||
*
|
||||
* <p>This handler provides an alternate way (other than {@link Server#setRequestLog(RequestLog)})
|
||||
* to log request, that can be applied to a particular handler (eg context).
|
||||
* This handler can be used to wrap an individual context for context logging, or can be listed
|
||||
* prior to a handler.
|
||||
* </p>
|
||||
* @see Server#setRequestLog(RequestLog)
|
||||
*/
|
||||
public class RequestLogHandler extends HandlerWrapper
|
||||
|
|
|
@ -158,19 +158,12 @@ public class SessionHandler extends ScopedHandler
|
|||
* has its session completed as the request exits the context.
|
||||
*/
|
||||
public class SessionAsyncListener implements AsyncListener
|
||||
{
|
||||
private Session _session;
|
||||
|
||||
public SessionAsyncListener(Session session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
{
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
//An async request has completed, so we can complete the session
|
||||
complete(((HttpServletRequest)event.getAsyncContext().getRequest()).getSession(false));
|
||||
complete(Request.getBaseRequest(event.getAsyncContext().getRequest()).getSession(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -182,7 +175,7 @@ public class SessionHandler extends ScopedHandler
|
|||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
complete(((HttpServletRequest)event.getAsyncContext().getRequest()).getSession(false));
|
||||
complete(Request.getBaseRequest(event.getAsyncContext().getRequest()).getSession(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -190,7 +183,6 @@ public class SessionHandler extends ScopedHandler
|
|||
{
|
||||
event.getAsyncContext().addListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
|
||||
|
@ -251,6 +243,7 @@ public class SessionHandler extends ScopedHandler
|
|||
|
||||
protected Scheduler _scheduler;
|
||||
protected boolean _ownScheduler = false;
|
||||
protected final SessionAsyncListener _sessionAsyncListener = new SessionAsyncListener();
|
||||
|
||||
|
||||
|
||||
|
@ -381,7 +374,7 @@ public class SessionHandler extends ScopedHandler
|
|||
{
|
||||
if (request.isAsyncStarted() && request.getDispatcherType() == DispatcherType.REQUEST)
|
||||
{
|
||||
request.getAsyncContext().addListener(new SessionAsyncListener(session));
|
||||
request.getAsyncContext().addListener(_sessionAsyncListener);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2018 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.server.handler;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/**
|
||||
* Testing oddball request scenarios (like error 400) where the error should
|
||||
* be logged
|
||||
*/
|
||||
@Tag("Unstable")
|
||||
@Disabled
|
||||
public class BadRequestLogHandlerTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(BadRequestLogHandlerTest.class);
|
||||
|
||||
public static class CaptureLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
public List<String> captured = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
int status = response.getCommittedMetaData().getStatus();
|
||||
captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getHttpURI(),request.getProtocol(),status));
|
||||
}
|
||||
}
|
||||
|
||||
private static class HelloHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().print("Hello World");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static Stream<Arguments> data()
|
||||
{
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
data.add(new String[]{ "GET /bad VER\r\n"
|
||||
+ "Host: localhost\r\n"
|
||||
+ "Connection: close\r\n\r\n" ,
|
||||
"GET <invalidrequest> HTTP/1.1 400" });
|
||||
data.add(new String[]{ "GET /%%adsasd HTTP/1.1\r\n"
|
||||
+ "Host: localhost\r\n"
|
||||
+ "Connection: close\r\n\r\n" ,
|
||||
"GET <invalidrequest> HTTP/1.1 400" });
|
||||
|
||||
return data.stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandler(String requestHeader, String expectedLog) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
requestLog.setHandler(new HelloHandler());
|
||||
|
||||
server.setHandler(requestLog);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
|
||||
InetAddress destAddr = InetAddress.getByName(host);
|
||||
int port = connector.getLocalPort();
|
||||
SocketAddress endpoint = new InetSocketAddress(destAddr,port);
|
||||
|
||||
Socket socket = new Socket();
|
||||
socket.setSoTimeout(1000);
|
||||
socket.connect(endpoint);
|
||||
|
||||
assertTimeoutPreemptively(Duration.ofSeconds(4), ()-> {
|
||||
try (OutputStream out = socket.getOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);
|
||||
InputStream in = socket.getInputStream();
|
||||
InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8))
|
||||
{
|
||||
StringReader request = new StringReader(requestHeader);
|
||||
IO.copy(request, writer);
|
||||
writer.flush();
|
||||
StringWriter response = new StringWriter();
|
||||
IO.copy(reader, response);
|
||||
LOG.info("Response: {}", response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
});
|
||||
|
||||
assertRequestLog(expectedLog, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRequestLog(final String expectedLog, CaptureLog captureLog)
|
||||
{
|
||||
int captureCount = captureLog.captured.size();
|
||||
|
||||
if (captureCount != 1)
|
||||
{
|
||||
LOG.warn("Capture Log size is {}, expected to be 1",captureCount);
|
||||
if (captureCount > 1)
|
||||
{
|
||||
for (int i = 0; i < captureCount; i++)
|
||||
{
|
||||
LOG.warn("[{}] {}",i,captureLog.captured.get(i));
|
||||
}
|
||||
}
|
||||
assertThat("Capture Log Entry Count",captureLog.captured.size(),is(1));
|
||||
}
|
||||
|
||||
String actual = captureLog.captured.get(0);
|
||||
assertThat("Capture Log",actual,is(expectedLog));
|
||||
}
|
||||
}
|
|
@ -1,886 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2018 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.server.handler;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.AsyncEvent;
|
||||
import javax.servlet.AsyncListener;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpChannelState;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/**
|
||||
* Tests for RequestLogHandler behavior.
|
||||
* <p>
|
||||
* Tests different request handler behavior against different server+error configurations
|
||||
*/
|
||||
@Tag("Unstable")
|
||||
@Disabled
|
||||
public class RequestLogHandlerTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(RequestLogHandlerTest.class);
|
||||
|
||||
public static class CaptureLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
public List<String> captured = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
int status = response.getCommittedMetaData().getStatus();
|
||||
captured.add(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class AbstractTestHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class HelloHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().print("Hello World");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResponseSendErrorHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.sendError(500,"Whoops");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServletExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new ServletException("Whoops");
|
||||
}
|
||||
}
|
||||
|
||||
private static class IOExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new IOException("Whoops");
|
||||
}
|
||||
}
|
||||
|
||||
private static class RuntimeExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new RuntimeException("Whoops");
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncOnTimeoutCompleteHandler extends AbstractTestHandler implements AsyncListener
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
ac.addListener(this);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
event.getAsyncContext().complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncOnTimeoutCompleteUnhandledHandler extends AbstractTestHandler implements AsyncListener
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
ac.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
event.getAsyncContext().complete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncOnTimeoutDispatchHandler extends AbstractTestHandler implements AsyncListener
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
if (request.getAttribute("deep") == null)
|
||||
{
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
ac.addListener(this);
|
||||
baseRequest.setHandled(true);
|
||||
request.setAttribute("deep",true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
event.getAsyncContext().dispatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static class AsyncOnStartIOExceptionHandler extends AbstractTestHandler implements AsyncListener
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
ac.addListener(this);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeout(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartAsync(AsyncEvent event) throws IOException
|
||||
{
|
||||
event.getAsyncContext().complete();
|
||||
throw new IOException("Whoops");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(AsyncEvent event) throws IOException
|
||||
{
|
||||
LOG.warn("onError() -> {}",event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete(AsyncEvent event) throws IOException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static class OKErrorHandler extends ErrorHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
if (baseRequest.isHandled() || response.isCommitted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// collect error details
|
||||
String reason = (response instanceof Response)?((Response)response).getReason():null;
|
||||
int status = response.getStatus();
|
||||
|
||||
// intentionally set response status to OK (this is a test to see what is actually logged)
|
||||
response.setStatus(200);
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.printf("Error %d: %s%n",status,reason);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static class DispatchErrorHandler extends ErrorHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
if (baseRequest.isHandled() || response.isCommitted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RequestDispatcher dispatcher = request.getRequestDispatcher("/errorok/");
|
||||
assertThat("Dispatcher", dispatcher, notNullValue());
|
||||
|
||||
try
|
||||
{
|
||||
dispatcher.forward(request,response);
|
||||
}
|
||||
catch (ServletException e)
|
||||
{
|
||||
throw new IOException("Dispatch.forward failed",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class AltErrorHandler extends ErrorHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
if (baseRequest.isHandled() || response.isCommitted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// collect error details
|
||||
String reason = (response instanceof Response)?((Response)response).getReason():null;
|
||||
int status = response.getStatus();
|
||||
|
||||
// intentionally set response status to OK (this is a test to see what is actually logged)
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.printf("Error %d: %s%n",status,reason);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static Stream<Arguments> data()
|
||||
{
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
data.add(new Object[] { new HelloHandler(), "/test/", "GET /test/ HTTP/1.1 200" });
|
||||
data.add(new Object[] { new AsyncOnTimeoutCompleteHandler(), "/test/", "GET /test/ HTTP/1.1 200" });
|
||||
data.add(new Object[] { new AsyncOnTimeoutCompleteUnhandledHandler(), "/test/", "GET /test/ HTTP/1.1 200" });
|
||||
data.add(new Object[] { new AsyncOnTimeoutDispatchHandler(), "/test/", "GET /test/ HTTP/1.1 200" });
|
||||
|
||||
data.add(new Object[] { new AsyncOnStartIOExceptionHandler(), "/test/", "GET /test/ HTTP/1.1 500" });
|
||||
data.add(new Object[] { new ResponseSendErrorHandler(), "/test/", "GET /test/ HTTP/1.1 500" });
|
||||
data.add(new Object[] { new ServletExceptionHandler(), "/test/", "GET /test/ HTTP/1.1 500" });
|
||||
data.add(new Object[] { new IOExceptionHandler(), "/test/", "GET /test/ HTTP/1.1 500" });
|
||||
data.add(new Object[] { new RuntimeExceptionHandler(), "/test/", "GET /test/ HTTP/1.1 500" });
|
||||
|
||||
return data.stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a RequestLogHandler at the end of a HandlerCollection. all other configuration on server at defaults.
|
||||
* @throws Exception if test failure
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollection(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { testHandler, requestLog });
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testMultipleLogHandlers(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[]{connector});
|
||||
|
||||
List<CaptureLog> captureLogs = new ArrayList<>();
|
||||
List<Handler> handlerList = new ArrayList<>();
|
||||
handlerList.add(testHandler);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
captureLogs.add(captureLog);
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
handlerList.add(requestLog);
|
||||
}
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(handlerList.toArray(new Handler[0]));
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
for (CaptureLog captureLog:captureLogs)
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a RequestLogHandler at the end of a HandlerCollection and also with the default ErrorHandler as server bean in place.
|
||||
* @throws Exception if test failure
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollection_ErrorHandler_ServerBean(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
ErrorHandler errorHandler = new ErrorHandler();
|
||||
server.addBean(errorHandler);
|
||||
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { testHandler, requestLog });
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
|
||||
* @throws Exception if test failure
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollection_AltErrorHandler(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
AltErrorHandler errorDispatcher = new AltErrorHandler();
|
||||
server.addBean(errorDispatcher);
|
||||
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
ContextHandler errorContext = new ContextHandler("/errorpage");
|
||||
errorContext.setHandler(new AltErrorHandler());
|
||||
ContextHandler testContext = new ContextHandler("/test");
|
||||
testContext.setHandler(testHandler);
|
||||
contexts.addHandler(errorContext);
|
||||
contexts.addHandler(testContext);
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { contexts, requestLog });
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
|
||||
* @throws Exception if test failure
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollection_OKErrorHandler(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
OKErrorHandler errorDispatcher = new OKErrorHandler();
|
||||
server.addBean(errorDispatcher);
|
||||
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
ContextHandler errorContext = new ContextHandler("/errorpage");
|
||||
errorContext.setHandler(new AltErrorHandler());
|
||||
ContextHandler testContext = new ContextHandler("/test");
|
||||
testContext.setHandler(testHandler);
|
||||
contexts.addHandler(errorContext);
|
||||
contexts.addHandler(testContext);
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { contexts, requestLog });
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a RequestLogHandler at the end of a HandlerCollection and also with the ErrorHandler in place.
|
||||
* @throws Exception if test failure
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollection_DispatchErrorHandler(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
DispatchErrorHandler errorDispatcher = new DispatchErrorHandler();
|
||||
server.addBean(errorDispatcher);
|
||||
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
ContextHandler errorContext = new ContextHandler("/errorok");
|
||||
errorContext.setHandler(new OKErrorHandler());
|
||||
ContextHandler testContext = new ContextHandler("/test");
|
||||
testContext.setHandler(testHandler);
|
||||
contexts.addHandler(errorContext);
|
||||
contexts.addHandler(testContext);
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { contexts, requestLog });
|
||||
server.setHandler(handlers);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.debug("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.debug("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerWrapped(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
Server server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
connector.setPort(0);
|
||||
server.setConnectors(new Connector[] { connector });
|
||||
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
requestLog.setHandler(testHandler);
|
||||
|
||||
server.setHandler(requestLog);
|
||||
|
||||
try (StacklessLogging ignore = new StacklessLogging(HttpChannel.class,HttpChannelState.class))
|
||||
{
|
||||
server.start();
|
||||
|
||||
String host = connector.getHost();
|
||||
if (host == null)
|
||||
{
|
||||
host = "localhost";
|
||||
}
|
||||
int port = connector.getLocalPort();
|
||||
|
||||
URI serverUri = new URI("http",null,host,port,requestPath,null,null);
|
||||
|
||||
// Make call to test handler
|
||||
HttpURLConnection connection = (HttpURLConnection)serverUri.toURL().openConnection();
|
||||
try
|
||||
{
|
||||
connection.setAllowUserInteraction(false);
|
||||
|
||||
// log response status code
|
||||
int statusCode = connection.getResponseCode();
|
||||
LOG.info("Response Status Code: {}",statusCode);
|
||||
|
||||
if (statusCode == 200)
|
||||
{
|
||||
// collect response message and log it
|
||||
String content = getResponseContent(connection);
|
||||
LOG.info("Response Content: {}",content);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
|
||||
assertRequestLog(expectedLogEntry, captureLog);
|
||||
}
|
||||
finally
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertRequestLog(final String expectedLogEntry, CaptureLog captureLog)
|
||||
{
|
||||
int captureCount = captureLog.captured.size();
|
||||
|
||||
if (captureCount != 1)
|
||||
{
|
||||
LOG.warn("Capture Log size is {}, expected to be 1",captureCount);
|
||||
if (captureCount > 1)
|
||||
{
|
||||
for (int i = 0; i < captureCount; i++)
|
||||
{
|
||||
LOG.warn("[{}] {}",i,captureLog.captured.get(i));
|
||||
}
|
||||
}
|
||||
assertThat("Capture Log Entry Count",captureLog.captured.size(),is(1));
|
||||
}
|
||||
|
||||
String actual = captureLog.captured.get(0);
|
||||
assertThat("Capture Log",actual,is(expectedLogEntry));
|
||||
}
|
||||
|
||||
private String getResponseContent(HttpURLConnection connection) throws IOException
|
||||
{
|
||||
try (InputStream in = connection.getInputStream(); InputStreamReader reader = new InputStreamReader(in))
|
||||
{
|
||||
StringWriter writer = new StringWriter();
|
||||
IO.copy(reader,writer);
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,31 +18,47 @@
|
|||
|
||||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Exchanger;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.servlet.AsyncContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.server.AbstractNCSARequestLog;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class RequestLogTest
|
||||
{
|
||||
Exchanger<String> _log;
|
||||
Log _log;
|
||||
Server _server;
|
||||
LocalConnector _connector;
|
||||
|
||||
|
@ -50,19 +66,35 @@ public class RequestLogTest
|
|||
@BeforeEach
|
||||
public void before() throws Exception
|
||||
{
|
||||
_log = new Exchanger<String>();
|
||||
_log = new Log();
|
||||
_server = new Server();
|
||||
_connector = new LocalConnector(_server);
|
||||
_server.addConnector(_connector);
|
||||
_server.setRequestLog(new Log());
|
||||
|
||||
}
|
||||
|
||||
void testHandlerServerStart() throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
_server.setHandler(new TestHandler());
|
||||
_server.start();
|
||||
}
|
||||
|
||||
private void startServer() throws Exception
|
||||
{
|
||||
_server.start();
|
||||
}
|
||||
|
||||
private void makeRequest(String requestPath) throws Exception
|
||||
{
|
||||
_connector.getResponse("GET "+requestPath+" HTTP/1.0\r\n\r\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@AfterEach
|
||||
public void after() throws Exception
|
||||
{
|
||||
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
|
@ -70,35 +102,41 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testNotHandled() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo HTTP/1.0\" 404 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestLine() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?data=1 HTTP/1.0\nhost: host:80\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?data=1 HTTP/1.0\" 200 "));
|
||||
|
||||
_connector.getResponse("GET //bad/foo?data=1 HTTP/1.0\n\n");
|
||||
log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET //bad/foo?data=1 HTTP/1.0\" 200 "));
|
||||
|
||||
_connector.getResponse("GET http://host:80/foo?data=1 HTTP/1.0\n\n");
|
||||
log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET http://host:80/foo?data=1 HTTP/1.0\" 200 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHTTP10Host() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse(
|
||||
"GET /foo?name=value HTTP/1.0\n"+
|
||||
"Host: servername\n"+
|
||||
"\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?name=value"));
|
||||
assertThat(log,containsString(" 200 "));
|
||||
}
|
||||
|
@ -106,11 +144,13 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testHTTP11() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse(
|
||||
"GET /foo?name=value HTTP/1.1\n"+
|
||||
"Host: servername\n"+
|
||||
"\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?name=value"));
|
||||
assertThat(log,containsString(" 200 "));
|
||||
}
|
||||
|
@ -118,11 +158,13 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testAbsolute() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse(
|
||||
"GET http://hostname:8888/foo?name=value HTTP/1.1\n"+
|
||||
"Host: servername\n"+
|
||||
"\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET http://hostname:8888/foo?name=value"));
|
||||
assertThat(log,containsString(" 200 "));
|
||||
}
|
||||
|
@ -130,8 +172,10 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testQuery() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?name=value HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?name=value"));
|
||||
assertThat(log,containsString(" 200 "));
|
||||
}
|
||||
|
@ -139,8 +183,10 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testSmallData() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?data=42 HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?"));
|
||||
assertThat(log,containsString(" 200 42 "));
|
||||
}
|
||||
|
@ -148,8 +194,10 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testBigData() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?data=102400 HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?"));
|
||||
assertThat(log,containsString(" 200 102400 "));
|
||||
}
|
||||
|
@ -157,8 +205,10 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testStatus() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?status=206 HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?"));
|
||||
assertThat(log,containsString(" 206 0 "));
|
||||
}
|
||||
|
@ -166,8 +216,10 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testStatusData() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo?status=206&data=42 HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo?"));
|
||||
assertThat(log,containsString(" 206 42 "));
|
||||
}
|
||||
|
@ -175,83 +227,460 @@ public class RequestLogTest
|
|||
@Test
|
||||
public void testBadRequest() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("XXXXXXXXXXXX\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("\"- - -\""));
|
||||
assertThat(log,containsString(" 400 0 "));
|
||||
assertThat(log,containsString(" 400 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadCharacter() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("METHOD /f\00o HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("\"- - -\""));
|
||||
assertThat(log,containsString(" 400 0 "));
|
||||
assertThat(log,containsString(" 400 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadVersion() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("METHOD /foo HTTP/9\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("\"- - -\""));
|
||||
assertThat(log,containsString(" 400 0 "));
|
||||
assertThat(log,containsString(" 400 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongURI() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
char[] chars = new char[10000];
|
||||
Arrays.fill(chars,'o');
|
||||
String ooo = new String(chars);
|
||||
_connector.getResponse("METHOD /f"+ooo+" HTTP/1.0\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("\"- - -\""));
|
||||
assertThat(log,containsString(" 414 0 "));
|
||||
assertThat(log,containsString(" 414 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongHeader() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
char[] chars = new char[10000];
|
||||
Arrays.fill(chars,'o');
|
||||
String ooo = new String(chars);
|
||||
_connector.getResponse("METHOD /foo HTTP/1.0\name: f+"+ooo+"\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("\"METHOD /foo HTTP/1.0\""));
|
||||
assertThat(log,containsString(" 431 0 "));
|
||||
assertThat(log,containsString(" 431 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadRequestNoHost() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET /foo HTTP/1.1\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET /foo "));
|
||||
assertThat(log,containsString(" 400 0 "));
|
||||
assertThat(log,containsString(" 400 "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseragentWithout() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET http://[:1]/foo HTTP/1.1\nReferer: http://other.site\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET http://[:1]/foo "));
|
||||
assertThat(log,containsString(" 400 0 \"http://other.site\" \"-\" - "));
|
||||
assertThat(log,containsString(" 400 50 \"http://other.site\" \"-\" - "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseragentWith() throws Exception
|
||||
{
|
||||
testHandlerServerStart();
|
||||
|
||||
_connector.getResponse("GET http://[:1]/foo HTTP/1.1\nReferer: http://other.site\nUser-Agent: Mozilla/5.0 (test)\n\n");
|
||||
String log = _log.exchange(null,5,TimeUnit.SECONDS);
|
||||
String log = _log.entries.poll(5,TimeUnit.SECONDS);
|
||||
assertThat(log,containsString("GET http://[:1]/foo "));
|
||||
assertThat(log,containsString(" 400 0 \"http://other.site\" \"Mozilla/5.0 (test)\" - "));
|
||||
assertThat(log,containsString(" 400 50 \"http://other.site\" \"Mozilla/5.0 (test)\" - "));
|
||||
}
|
||||
|
||||
|
||||
// Tests from here use these parameters
|
||||
public static Stream<Arguments> data()
|
||||
{
|
||||
List<Object[]> data = new ArrayList<>();
|
||||
|
||||
data.add(new Object[] { new NoopHandler(), "/noop", "\"GET /noop HTTP/1.0\" 404" });
|
||||
data.add(new Object[] { new HelloHandler(), "/hello", "\"GET /hello HTTP/1.0\" 200" });
|
||||
data.add(new Object[] { new ResponseSendErrorHandler(), "/sendError", "\"GET /sendError HTTP/1.0\" 599" });
|
||||
data.add(new Object[] { new ServletExceptionHandler(), "/sex", "\"GET /sex HTTP/1.0\" 500" });
|
||||
data.add(new Object[] { new IOExceptionHandler(), "/ioex", "\"GET /ioex HTTP/1.0\" 500" });
|
||||
data.add(new Object[] { new RuntimeExceptionHandler(), "/rtex", "\"GET /rtex HTTP/1.0\" 500" });
|
||||
data.add(new Object[] { new BadMessageHandler(), "/bad", "\"GET /bad HTTP/1.0\" 499" });
|
||||
data.add(new Object[] { new AbortHandler(), "/bad", "\"GET /bad HTTP/1.0\" 488" });
|
||||
|
||||
return data.stream().map(Arguments::of);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testServerRequestLog(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
_server.setHandler(testHandler);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerWrapper(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
RequestLogHandler handler = new RequestLogHandler();
|
||||
handler.setRequestLog(_log);
|
||||
handler.setHandler(testHandler);
|
||||
_server.setHandler(handler);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollectionFirst(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
RequestLogHandler handler = new RequestLogHandler();
|
||||
handler.setRequestLog(_log);
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { handler, testHandler });
|
||||
_server.setHandler(handlers);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testLogHandlerCollectionLast(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
RequestLogHandler handler = new RequestLogHandler();
|
||||
handler.setRequestLog(_log);
|
||||
// This is the old ordering of request handler and it cannot well handle thrown exception
|
||||
Assumptions.assumeTrue(
|
||||
testHandler instanceof NoopHandler ||
|
||||
testHandler instanceof HelloHandler ||
|
||||
testHandler instanceof ResponseSendErrorHandler
|
||||
);
|
||||
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
handlers.setHandlers(new Handler[] { testHandler, handler });
|
||||
_server.setHandler(handlers);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testErrorHandler(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
AbstractHandler.ErrorDispatchHandler wrapper = new AbstractHandler.ErrorDispatchHandler()
|
||||
{
|
||||
@Override
|
||||
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
testHandler.handle(target,baseRequest,request,response);
|
||||
}
|
||||
};
|
||||
|
||||
_server.setHandler(wrapper);
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
ErrorHandler errorHandler = new ErrorHandler()
|
||||
{
|
||||
@Override
|
||||
public void doError(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
errors.add(baseRequest.getRequestURI());
|
||||
super.doError(target, baseRequest, request, response);
|
||||
}
|
||||
};
|
||||
_server.addBean(errorHandler);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
|
||||
if (!(testHandler instanceof HelloHandler))
|
||||
assertThat(errors,contains(requestPath));
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testOKErrorHandler(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
AbstractHandler.ErrorDispatchHandler wrapper = new AbstractHandler.ErrorDispatchHandler()
|
||||
{
|
||||
@Override
|
||||
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
testHandler.handle(target,baseRequest,request,response);
|
||||
}
|
||||
};
|
||||
|
||||
_server.setHandler(wrapper);
|
||||
|
||||
ErrorHandler errorHandler = new OKErrorHandler();
|
||||
_server.addBean(errorHandler);
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
|
||||
expectedLogEntry = "\"GET " + requestPath + " HTTP/1.0\" 200";
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testAsyncDispatch(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
_server.setHandler(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
if (Boolean.TRUE.equals(request.getAttribute("ASYNC")))
|
||||
testHandler.handle(target,baseRequest,request,response);
|
||||
else
|
||||
{
|
||||
request.setAttribute("ASYNC",Boolean.TRUE);
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
ac.dispatch();
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("data")
|
||||
public void testAsyncComplete(Handler testHandler, String requestPath, String expectedLogEntry) throws Exception
|
||||
{
|
||||
_server.setRequestLog(_log);
|
||||
_server.setHandler(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
if (Boolean.TRUE.equals(request.getAttribute("ASYNC")))
|
||||
testHandler.handle(target,baseRequest,request,response);
|
||||
else
|
||||
{
|
||||
request.setAttribute("ASYNC",Boolean.TRUE);
|
||||
AsyncContext ac = request.startAsync();
|
||||
ac.setTimeout(1000);
|
||||
baseRequest.setHandled(true);
|
||||
_server.getThreadPool().execute(()->
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
baseRequest.setHandled(false);
|
||||
testHandler.handle(target, baseRequest, request, response);
|
||||
if (!baseRequest.isHandled())
|
||||
response.sendError(404);
|
||||
}
|
||||
catch (BadMessageException bad)
|
||||
{
|
||||
response.sendError(bad.getCode());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
response.sendError(500);
|
||||
}
|
||||
}
|
||||
catch(Throwable th)
|
||||
{
|
||||
throw new RuntimeException(th);
|
||||
}
|
||||
ac.complete();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
startServer();
|
||||
makeRequest(requestPath);
|
||||
assertRequestLog(expectedLogEntry, _log);
|
||||
}
|
||||
|
||||
|
||||
private void assertRequestLog(final String expectedLogEntry, Log log) throws Exception
|
||||
{
|
||||
String line = log.entries.poll(5, TimeUnit.SECONDS);
|
||||
Assertions.assertNotNull(line);
|
||||
assertThat(line,containsString(expectedLogEntry));
|
||||
Assertions.assertTrue(log.entries.isEmpty());
|
||||
}
|
||||
|
||||
public static class CaptureLog extends AbstractLifeCycle implements RequestLog
|
||||
{
|
||||
public BlockingQueue<String> log = new BlockingArrayQueue<>();
|
||||
|
||||
@Override
|
||||
public void log(Request request, Response response)
|
||||
{
|
||||
int status = response.getCommittedMetaData().getStatus();
|
||||
log.add(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class AbstractTestHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoopHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static class HelloHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().print("Hello World");
|
||||
if (baseRequest!=null)
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ResponseSendErrorHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.sendError(599,"expected");
|
||||
if (baseRequest!=null)
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ServletExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new ServletException("expected");
|
||||
}
|
||||
}
|
||||
|
||||
private static class IOExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new IOException("expected");
|
||||
}
|
||||
}
|
||||
|
||||
private static class RuntimeExceptionHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new RuntimeException("expected");
|
||||
}
|
||||
}
|
||||
|
||||
private static class BadMessageHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
throw new BadMessageException(499);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AbortHandler extends AbstractTestHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
BadMessageException bad = new BadMessageException(488);
|
||||
baseRequest.getHttpChannel().abort(bad);
|
||||
throw bad;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OKErrorHandler extends ErrorHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
if (baseRequest.isHandled() || response.isCommitted())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// collect error details
|
||||
String reason = (response instanceof Response)?((Response)response).getReason():null;
|
||||
int status = response.getStatus();
|
||||
|
||||
// intentionally set response status to OK (this is a test to see what is actually logged)
|
||||
response.setStatus(200);
|
||||
response.setContentType("text/plain");
|
||||
PrintWriter out = response.getWriter();
|
||||
out.printf("Error %d: %s%n",status,reason);
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Log extends AbstractNCSARequestLog
|
||||
{
|
||||
public BlockingQueue<String> entries = new BlockingArrayQueue<>();
|
||||
|
||||
Log()
|
||||
{
|
||||
super.setExtended(true);
|
||||
super.setLogLatency(true);
|
||||
|
@ -269,7 +698,7 @@ public class RequestLogTest
|
|||
{
|
||||
try
|
||||
{
|
||||
_log.exchange(requestEntry);
|
||||
entries.add(requestEntry);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -277,7 +706,7 @@ public class RequestLogTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class TestHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
|
@ -286,7 +715,7 @@ public class RequestLogTest
|
|||
String q = request.getQueryString();
|
||||
if (q==null)
|
||||
return;
|
||||
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
for (String action : q.split("\\&"))
|
||||
{
|
||||
|
@ -300,12 +729,12 @@ public class RequestLogTest
|
|||
response.setStatus(Integer.parseInt(value));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case "data":
|
||||
{
|
||||
int data = Integer.parseInt(value);
|
||||
PrintWriter out = response.getWriter();
|
||||
|
||||
|
||||
int w=0;
|
||||
while (w<data)
|
||||
{
|
||||
|
@ -343,7 +772,7 @@ public class RequestLogTest
|
|||
response.flushBuffer();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case "read":
|
||||
{
|
||||
InputStream in = request.getInputStream();
|
||||
|
|
|
@ -53,14 +53,12 @@ import javax.servlet.http.HttpServletResponseWrapper;
|
|||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.DebugListener;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpChannel.Listener;
|
||||
import org.eclipse.jetty.server.QuietServletException;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.RequestLog;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
|
@ -99,15 +97,13 @@ public class AsyncServletTest
|
|||
|
||||
_log=new ArrayList<>();
|
||||
RequestLog log=new Log();
|
||||
RequestLogHandler logHandler = new RequestLogHandler();
|
||||
logHandler.setRequestLog(log);
|
||||
_server.setHandler(logHandler);
|
||||
_server.setRequestLog(log);
|
||||
_expectedLogs=1;
|
||||
_expectedCode="200 ";
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||
context.setContextPath("/ctx");
|
||||
logHandler.setHandler(context);
|
||||
_server.setHandler(context);
|
||||
context.addEventListener(new DebugListener());
|
||||
|
||||
_errorHandler = new ErrorPageErrorHandler();
|
||||
|
|
|
@ -53,7 +53,6 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
|||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.ErrorHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.toolchain.test.IO;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -313,12 +312,9 @@ public class ServletRequestLogTest
|
|||
|
||||
// Next the behavior as defined by etc/jetty-requestlog.xml
|
||||
// the id="RequestLog"
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
server.setRequestLog(captureLog);
|
||||
|
||||
handlers.addHandler(requestLog);
|
||||
|
||||
// Lastly, the behavior as defined by deployment of a webapp
|
||||
// Add the Servlet Context
|
||||
ServletContextHandler app = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
|
@ -404,12 +400,9 @@ public class ServletRequestLogTest
|
|||
|
||||
// Next the behavior as defined by etc/jetty-requestlog.xml
|
||||
// the id="RequestLog"
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
server.setRequestLog(captureLog);
|
||||
|
||||
handlers.addHandler(requestLog);
|
||||
|
||||
// Lastly, the behavior as defined by deployment of a webapp
|
||||
// Add the Servlet Context
|
||||
ServletContextHandler app = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
|
@ -493,12 +486,9 @@ public class ServletRequestLogTest
|
|||
|
||||
// Next the behavior as defined by etc/jetty-requestlog.xml
|
||||
// the id="RequestLog"
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
server.setRequestLog(captureLog);
|
||||
|
||||
handlers.addHandler(requestLog);
|
||||
|
||||
// Lastly, the behavior as defined by deployment of a webapp
|
||||
// Add the Servlet Context
|
||||
ServletContextHandler app = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
|
@ -587,14 +577,9 @@ public class ServletRequestLogTest
|
|||
|
||||
// Next the proposed behavioral change to etc/jetty-requestlog.xml
|
||||
// the id="RequestLog"
|
||||
RequestLogHandler requestLog = new RequestLogHandler();
|
||||
CaptureLog captureLog = new CaptureLog();
|
||||
requestLog.setRequestLog(captureLog);
|
||||
|
||||
Handler origServerHandler = server.getHandler();
|
||||
requestLog.setHandler(origServerHandler);
|
||||
server.setHandler(requestLog);
|
||||
|
||||
server.setRequestLog(captureLog);
|
||||
|
||||
// Lastly, the behavior as defined by deployment of a webapp
|
||||
// Add the Servlet Context
|
||||
ServletContextHandler app = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -40,7 +40,7 @@
|
|||
<!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
|
||||
<alpn.version>undefined</alpn.version>
|
||||
<conscrypt.version>1.1.4</conscrypt.version>
|
||||
<asm.version>7.0-beta</asm.version>
|
||||
<asm.version>7.0</asm.version>
|
||||
<jmh.version>1.21</jmh.version>
|
||||
<jmhjar.name>benchmarks</jmhjar.name>
|
||||
<tycho-version>1.2.0</tycho-version>
|
||||
|
|
|
@ -18,15 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.http.client;
|
||||
|
||||
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -65,6 +56,15 @@ import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
|
||||
public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||
{
|
||||
@Override
|
||||
|
@ -344,13 +344,14 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
|||
}
|
||||
});
|
||||
|
||||
scenario.client.setIdleTimeout(idleTimeout);
|
||||
scenario.client.setIdleTimeout(2 * idleTimeout);
|
||||
|
||||
byte[] content = new byte[1024];
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
scenario.client.newRequest(scenario.newURI())
|
||||
.header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString())
|
||||
.content(new BytesContentProvider(content))
|
||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||
.send(new BufferingResponseListener()
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -18,11 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.http.client;
|
||||
|
||||
import static org.eclipse.jetty.http.client.Transport.UNIX_SOCKET;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -30,11 +27,11 @@ import java.util.Locale;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
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;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -68,6 +65,9 @@ import org.hamcrest.Matchers;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTransportScenario>
|
||||
{
|
||||
private final Logger logger = Log.getLogger(HttpClientLoadTest.class);
|
||||
|
@ -186,7 +186,7 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
// Choose a random method
|
||||
HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;
|
||||
|
||||
boolean ssl = scenario.isTransportSecure();
|
||||
boolean ssl = scenario.transport.isTlsBased();
|
||||
|
||||
// Choose randomly whether to close the connection on the client or on the server
|
||||
boolean clientClose = false;
|
||||
|
@ -196,13 +196,17 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
if (!ssl && random.nextInt(100) < 5)
|
||||
serverClose = true;
|
||||
|
||||
long clientTimeout = 0;
|
||||
// if (!ssl && random.nextInt(100) < 5)
|
||||
// clientTimeout = random.nextInt(500) + 500;
|
||||
|
||||
int maxContentLength = 64 * 1024;
|
||||
int contentLength = random.nextInt(maxContentLength) + 1;
|
||||
|
||||
test(scenario.getScheme(), host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
|
||||
test(scenario.getScheme(), host, method.asString(), clientClose, serverClose, clientTimeout, 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)
|
||||
private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, long clientTimeout, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures)
|
||||
{
|
||||
long requestId = requestCount.incrementAndGet();
|
||||
Request request = scenario.client.newRequest(host, scenario.getNetworkConnectorLocalPortInt().orElse(0))
|
||||
|
@ -215,6 +219,12 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
else if (serverClose)
|
||||
request.header("X-Close", "true");
|
||||
|
||||
if (clientTimeout > 0)
|
||||
{
|
||||
request.header("X-Timeout", String.valueOf(clientTimeout));
|
||||
request.idleTimeout(clientTimeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case "GET":
|
||||
|
@ -254,12 +264,18 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
{
|
||||
if (result.isFailed())
|
||||
{
|
||||
result.getFailure().printStackTrace();
|
||||
failures.add("Result failed " + result);
|
||||
Throwable failure = result.getFailure();
|
||||
if (!(clientTimeout > 0 && failure instanceof TimeoutException))
|
||||
{
|
||||
failure.printStackTrace();
|
||||
failures.add("Result failed " + result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (checkContentLength && contentLength.get() != 0)
|
||||
failures.add("Content length mismatch " + contentLength);
|
||||
}
|
||||
|
||||
if (checkContentLength && contentLength.get() != 0)
|
||||
failures.add("Content length mismatch " + contentLength);
|
||||
|
||||
requestLatch.countDown();
|
||||
latch.countDown();
|
||||
|
@ -288,8 +304,14 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
private class LoadHandler extends AbstractHandler
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
String timeout = request.getHeader("X-Timeout");
|
||||
if (timeout != null)
|
||||
sleep(2 * Integer.parseInt(timeout));
|
||||
|
||||
String method = request.getMethod().toUpperCase(Locale.ENGLISH);
|
||||
switch (method)
|
||||
{
|
||||
|
@ -313,8 +335,18 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
|
||||
if (Boolean.parseBoolean(request.getHeader("X-Close")))
|
||||
response.setHeader("Connection", "close");
|
||||
}
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
private void sleep(long time) throws InterruptedIOException
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(time);
|
||||
}
|
||||
catch (InterruptedException x)
|
||||
{
|
||||
throw new InterruptedIOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,8 +361,9 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
|||
}
|
||||
|
||||
@Override
|
||||
public Connector newServerConnector( Server server) throws Exception {
|
||||
if (transport == UNIX_SOCKET)
|
||||
public Connector newServerConnector( Server server)
|
||||
{
|
||||
if (transport == Transport.UNIX_SOCKET)
|
||||
{
|
||||
UnixSocketConnector
|
||||
unixSocketConnector = new UnixSocketConnector( server, provideServerConnectionFactory( transport ));
|
||||
|
|
|
@ -20,9 +20,14 @@ package org.eclipse.jetty.http.client;
|
|||
|
||||
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
||||
import static org.eclipse.jetty.http.client.Transport.UNIX_SOCKET;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -56,10 +61,10 @@ import org.eclipse.jetty.server.Handler;
|
|||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
|
@ -616,6 +621,7 @@ public class ServerTimeoutsTest extends AbstractTest<TransportScenario>
|
|||
{
|
||||
init(transport);
|
||||
int bytesPerSecond = 20;
|
||||
scenario.requestLog.clear();
|
||||
scenario.httpConfig.setMinRequestDataRate(bytesPerSecond);
|
||||
CountDownLatch handlerLatch = new CountDownLatch(1);
|
||||
scenario.start(new AbstractHandler.ErrorDispatchHandler()
|
||||
|
@ -643,13 +649,15 @@ public class ServerTimeoutsTest extends AbstractTest<TransportScenario>
|
|||
});
|
||||
|
||||
DeferredContentProvider contentProvider = new DeferredContentProvider();
|
||||
CountDownLatch resultLatch = new CountDownLatch(1);
|
||||
BlockingQueue<Object> results = new BlockingArrayQueue<>();
|
||||
scenario.client.newRequest(scenario.newURI())
|
||||
.content(contentProvider)
|
||||
.send(result ->
|
||||
{
|
||||
if (result.getResponse().getStatus() == HttpStatus.REQUEST_TIMEOUT_408)
|
||||
resultLatch.countDown();
|
||||
if (result.isFailed())
|
||||
results.offer(result.getFailure());
|
||||
else
|
||||
results.offer(result.getResponse().getStatus());
|
||||
});
|
||||
|
||||
for (int i = 0; i < 3; ++i)
|
||||
|
@ -659,9 +667,17 @@ public class ServerTimeoutsTest extends AbstractTest<TransportScenario>
|
|||
}
|
||||
contentProvider.close();
|
||||
|
||||
assertThat(scenario.requestLog.poll(5,TimeUnit.SECONDS), containsString(" 408"));
|
||||
|
||||
// Request should timeout.
|
||||
assertTrue(handlerLatch.await(5, TimeUnit.SECONDS));
|
||||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
Object result = results.poll(5, TimeUnit.SECONDS);
|
||||
assertNotNull(result);
|
||||
if (result instanceof Integer)
|
||||
assertThat((Integer)result,is(408));
|
||||
else
|
||||
assertThat(result,instanceOf(Throwable.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.nio.file.Path;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
||||
|
@ -56,6 +57,7 @@ import org.eclipse.jetty.servlet.ServletHolder;
|
|||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.unixsocket.UnixSocketConnector;
|
||||
import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
|
||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -75,6 +77,7 @@ public class TransportScenario
|
|||
protected String servletPath = "/servlet";
|
||||
protected HttpClient client;
|
||||
protected Path sockFile;
|
||||
protected final BlockingQueue<String> requestLog= new BlockingArrayQueue<>();
|
||||
|
||||
public TransportScenario(final Transport transport) throws IOException
|
||||
{
|
||||
|
@ -320,7 +323,15 @@ public class TransportScenario
|
|||
server.addBean(mbeanContainer);
|
||||
connector = newServerConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
server.setRequestLog((request, response) ->
|
||||
{
|
||||
int status = response.getCommittedMetaData().getStatus();
|
||||
requestLog.offer(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
|
||||
});
|
||||
|
||||
server.setHandler(handler);
|
||||
|
||||
try
|
||||
{
|
||||
server.start();
|
||||
|
@ -375,4 +386,6 @@ public class TransportScenario
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
|||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.server.handler.RequestLogHandler;
|
||||
import org.eclipse.jetty.server.handler.ResourceHandler;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionCache;
|
||||
import org.eclipse.jetty.server.session.FileSessionDataStore;
|
||||
|
@ -107,9 +106,8 @@ public class TestServer
|
|||
// Handlers
|
||||
HandlerCollection handlers = new HandlerCollection();
|
||||
ContextHandlerCollection contexts = new ContextHandlerCollection();
|
||||
RequestLogHandler requestLogHandler = new RequestLogHandler();
|
||||
handlers.setHandlers(new Handler[]
|
||||
{ contexts, new DefaultHandler(), requestLogHandler });
|
||||
{ contexts, new DefaultHandler() });
|
||||
|
||||
// Add restart handler to test the ability to save sessions and restart
|
||||
RestartHandler restart = new RestartHandler();
|
||||
|
@ -127,7 +125,7 @@ public class TestServer
|
|||
File log=File.createTempFile("jetty-yyyy_mm_dd", "log");
|
||||
NCSARequestLog requestLog = new NCSARequestLog(log.toString());
|
||||
requestLog.setExtended(false);
|
||||
requestLogHandler.setRequestLog(requestLog);
|
||||
server.setRequestLog(requestLog);
|
||||
|
||||
server.setStopAtShutdown(true);
|
||||
|
||||
|
|
Loading…
Reference in New Issue