diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java b/jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java
index 8135dfbc5b8..5a58abc84cf 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java
@@ -31,12 +31,14 @@ import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.QuotedCSV;
import org.eclipse.jetty.http.pathmap.PathMappings;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -233,8 +235,9 @@ import static java.lang.invoke.MethodType.methodType;
*
* %{d}u |
*
- * Remote user if the request was authenticated. May be bogus if return status (%s) is 401 (unauthorized).
- * Optional parameter d, with this parameter deferred authentication will also be checked.
+ * Remote user if the request was authenticated with servlet authentication. May be bogus if return status (%s) is 401 (unauthorized).
+ * Optional parameter d, with this parameter deferred authentication will also be checked,
+ * this is equivalent to {@link HttpServletRequest#getRemoteUser()}.
* |
*
*
@@ -294,11 +297,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
{
_logHandle = getLogHandle(formatString);
}
- catch (NoSuchMethodException e)
- {
- throw new IllegalStateException(e);
- }
- catch (IllegalAccessException e)
+ catch (NoSuchMethodException | IllegalAccessException e)
{
throw new IllegalStateException(e);
}
@@ -357,20 +356,14 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
protected static String getAuthentication(Request request, boolean checkDeferred)
{
Authentication authentication = request.getAuthentication();
+ if (checkDeferred && authentication instanceof Authentication.Deferred)
+ authentication = ((Authentication.Deferred)authentication).authenticate(request);
String name = null;
-
- boolean deferred = false;
- if (checkDeferred && authentication instanceof Authentication.Deferred)
- {
- authentication = ((Authentication.Deferred)authentication).authenticate(request);
- deferred = true;
- }
-
if (authentication instanceof Authentication.User)
name = ((Authentication.User)authentication).getUserIdentity().getUserPrincipal().getName();
- return (name == null) ? null : (deferred ? ("?" + name) : name);
+ return name;
}
/**
@@ -415,9 +408,9 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
if (_ignorePaths != null && _ignorePaths.length > 0)
{
_ignorePathMap = new PathMappings<>();
- for (int i = 0; i < _ignorePaths.length; i++)
+ for (String ignorePath : _ignorePaths)
{
- _ignorePathMap.put(_ignorePaths[i], _ignorePaths[i]);
+ _ignorePathMap.put(ignorePath, ignorePath);
}
}
else
@@ -442,8 +435,8 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
private MethodHandle getLogHandle(String formatString) throws NoSuchMethodException, IllegalAccessException
{
MethodHandles.Lookup lookup = MethodHandles.lookup();
- MethodHandle append = lookup.findStatic(CustomRequestLog.class, "append", methodType(Void.TYPE, String.class, StringBuilder.class));
- MethodHandle logHandle = lookup.findStatic(CustomRequestLog.class, "logNothing", methodType(Void.TYPE, StringBuilder.class, Request.class, Response.class));
+ MethodHandle append = lookup.findStatic(CustomRequestLog.class, "append", methodType(void.class, String.class, StringBuilder.class));
+ MethodHandle logHandle = lookup.findStatic(CustomRequestLog.class, "logNothing", methodType(void.class, StringBuilder.class, Request.class, Response.class));
List tokens = getTokens(formatString);
Collections.reverse(tokens);
@@ -486,7 +479,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
String arg = m.group("ARG");
String modifierString = m.group("MOD");
- Boolean negated = false;
+ boolean negated = false;
if (modifierString != null)
{
if (modifierString.startsWith("!"))
@@ -581,8 +574,8 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
private MethodHandle updateLogHandle(MethodHandle logHandle, MethodHandle append, MethodHandles.Lookup lookup, String code, String arg, List modifiers, boolean negated) throws NoSuchMethodException, IllegalAccessException
{
- MethodType logType = methodType(Void.TYPE, StringBuilder.class, Request.class, Response.class);
- MethodType logTypeArg = methodType(Void.TYPE, String.class, StringBuilder.class, Request.class, Response.class);
+ MethodType logType = methodType(void.class, StringBuilder.class, Request.class, Response.class);
+ MethodType logTypeArg = methodType(void.class, String.class, StringBuilder.class, Request.class, Response.class);
//TODO should we throw IllegalArgumentExceptions when given arguments for codes which do not take them
MethodHandle specificHandle;
@@ -596,7 +589,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "a":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
arg = "server";
String method;
@@ -628,7 +621,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "p":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
arg = "server";
String method;
@@ -662,7 +655,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "I":
{
String method;
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
method = "logBytesReceived";
else if (arg.equalsIgnoreCase("clf"))
method = "logBytesReceivedCLF";
@@ -676,7 +669,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "O":
{
String method;
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
method = "logBytesSent";
else if (arg.equalsIgnoreCase("clf"))
method = "logBytesSentCLF";
@@ -690,7 +683,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "S":
{
String method;
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
method = "logBytesTransferred";
else if (arg.equalsIgnoreCase("clf"))
method = "logBytesTransferredCLF";
@@ -703,7 +696,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "C":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
{
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestCookies", logType);
}
@@ -723,7 +716,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "e":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %e");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logEnvironmentVar", logTypeArg);
@@ -745,7 +738,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "i":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %i");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestHeader", logTypeArg);
@@ -767,7 +760,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "o":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %o");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logResponseHeader", logTypeArg);
@@ -832,7 +825,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
DateCache logDateCache = new DateCache(format, locale, timeZone);
- MethodType logTypeDateCache = methodType(Void.TYPE, DateCache.class, StringBuilder.class, Request.class, Response.class);
+ MethodType logTypeDateCache = methodType(void.class, DateCache.class, StringBuilder.class, Request.class, Response.class);
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestTime", logTypeDateCache);
specificHandle = specificHandle.bindTo(logDateCache);
break;
@@ -866,10 +859,12 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "u":
{
String method;
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
+ method = "logRequestAuthentication";
+ else if ("d".equals(arg))
method = "logRequestAuthenticationWithDeferred";
else
- method = "logRequestAuthentication";
+ throw new IllegalArgumentException("Invalid arg for %u: " + arg);
specificHandle = lookup.findStatic(CustomRequestLog.class, method, logType);
break;
@@ -889,7 +884,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "ti":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %ti");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logRequestTrailer", logTypeArg);
@@ -899,7 +894,7 @@ public class CustomRequestLog extends ContainerLifeCycle implements RequestLog
case "to":
{
- if (arg == null || arg.isEmpty())
+ if (StringUtil.isEmpty(arg))
throw new IllegalArgumentException("No arg for %to");
specificHandle = lookup.findStatic(CustomRequestLog.class, "logResponseTrailer", logTypeArg);
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/CustomRequestLogTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/CustomRequestLogTest.java
similarity index 87%
rename from jetty-server/src/test/java/org/eclipse/jetty/server/handler/CustomRequestLogTest.java
rename to tests/test-integration/src/test/java/org/eclipse/jetty/test/CustomRequestLogTest.java
index b2a816e887d..32769fc0fd9 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/CustomRequestLogTest.java
+++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/CustomRequestLogTest.java
@@ -16,7 +16,7 @@
// ========================================================================
//
-package org.eclipse.jetty.server.handler;
+package org.eclipse.jetty.test;
import java.io.IOException;
import java.io.InputStream;
@@ -26,15 +26,25 @@ import java.net.NetworkInterface;
import java.net.Socket;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.Base64;
import java.util.Enumeration;
import java.util.Locale;
+import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.security.UserStore;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
import org.eclipse.jetty.server.CustomRequestLog;
import org.eclipse.jetty.server.ForwardedRequestCustomizer;
import org.eclipse.jetty.server.HttpConnectionFactory;
@@ -44,8 +54,12 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.DateCache;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Credential;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
@@ -88,24 +102,70 @@ public class CustomRequestLogTest
TestRequestLogWriter writer = new TestRequestLogWriter();
_log = new CustomRequestLog(writer, formatString);
_server.setRequestLog(_log);
- _server.setHandler(new TestHandler());
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.setSecurityHandler(getSecurityHandler("username", "password", "testRealm"));
+ contextHandler.addServlet(new ServletHolder(new TestServlet()), "/");
+ _server.setHandler(contextHandler);
_server.start();
String host = _serverConnector.getHost();
if (host == null)
- {
host = "localhost";
- }
+
int localPort = _serverConnector.getLocalPort();
_serverURI = new URI(String.format("http://%s:%d/", host, localPort));
}
+ private static SecurityHandler getSecurityHandler(String username, String password, String realm)
+ {
+ HashLoginService loginService = new HashLoginService();
+ UserStore userStore = new UserStore();
+ userStore.addUser(username, Credential.getCredential(password), new String[]{"user"});
+ loginService.setUserStore(userStore);
+ loginService.setName(realm);
+
+ Constraint constraint = new Constraint();
+ constraint.setName("auth");
+ constraint.setAuthenticate(true);
+ constraint.setRoles(new String[]{"**"});
+
+ ConstraintMapping mapping = new ConstraintMapping();
+ mapping.setPathSpec("/secure/*");
+ mapping.setConstraint(constraint);
+
+ ConstraintSecurityHandler security = new ConstraintSecurityHandler();
+ security.addConstraintMapping(mapping);
+ security.setAuthenticator(new BasicAuthenticator());
+ security.setLoginService(loginService);
+
+ return security;
+ }
+
@AfterEach
public void after() throws Exception
{
_server.stop();
}
+ @Test
+ public void testLogRemoteUser() throws Exception
+ {
+ String authHeader = HttpHeader.AUTHORIZATION + ": Basic " + Base64.getEncoder().encodeToString("username:password".getBytes());
+ testHandlerServerStart("%u %{d}u");
+
+ _connector.getResponse("GET / HTTP/1.0\n\n\n");
+ String log = _entries.poll(5, TimeUnit.SECONDS);
+ assertThat(log, is("- -"));
+
+ _connector.getResponse("GET / HTTP/1.0\n" + authHeader + "\n\n\n");
+ log = _entries.poll(5, TimeUnit.SECONDS);
+ assertThat(log, is("- username"));
+
+ _connector.getResponse("GET /secure HTTP/1.0\n" + authHeader + "\n\n\n");
+ log = _entries.poll(5, TimeUnit.SECONDS);
+ assertThat(log, is("username username"));
+ }
+
@Test
public void testModifier() throws Exception
{
@@ -374,7 +434,7 @@ public class CustomRequestLogTest
_connector.getResponse("GET / HTTP/1.0\n\n");
String log = _entries.poll(5, TimeUnit.SECONDS);
long requestTime = requestTimes.poll(5, TimeUnit.SECONDS);
- DateCache dateCache = new DateCache(_log.DEFAULT_DATE_FORMAT, Locale.getDefault(), "GMT");
+ DateCache dateCache = new DateCache(CustomRequestLog.DEFAULT_DATE_FORMAT, Locale.getDefault(), "GMT");
assertThat(log, is("RequestTime: [" + dateCache.format(requestTime) + "]"));
}
@@ -549,11 +609,13 @@ public class CustomRequestLogTest
}
}
- private class TestHandler extends AbstractHandler
+ private class TestServlet extends HttpServlet
{
@Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
+ Request baseRequest = Objects.requireNonNull(Request.getBaseRequest(request));
+
if (request.getRequestURI().contains("error404"))
{
response.setStatus(404);
@@ -596,10 +658,7 @@ public class CustomRequestLogTest
if (request.getContentLength() > 0)
{
InputStream in = request.getInputStream();
- while (in.read() > 0)
- {
- ;
- }
+ while (in.read() > 0);
}
}
}