Skip to content

Commit

Permalink
Adding demo of how to access Request fields during RequestLog.
Browse files Browse the repository at this point in the history
  • Loading branch information
joakime committed Jan 20, 2025
1 parent 1879da3 commit 48fe422
Show file tree
Hide file tree
Showing 3 changed files with 313 additions and 0 deletions.
120 changes: 120 additions & 0 deletions embedded/requestlog/src/main/java/examples/ParamRequestLog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package examples;

import java.io.IOException;
import java.util.concurrent.ExecutionException;

import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.server.FormFields;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.RequestLog;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ParamRequestLog extends ContainerLifeCycle implements RequestLog
{
private static final Logger LOG = LoggerFactory.getLogger(ParamRequestLog.class);
private final RequestLog.Writer logWriter;

public ParamRequestLog(RequestLog.Writer requestLogWriter)
{
this.logWriter = requestLogWriter;
installBean(this.logWriter);
}

@Override
public void log(Request request, Response response)
{
try
{
StringBuilder entry = new StringBuilder();

entry.append(request.getMethod());
entry.append(" ");
entry.append(request.getHttpURI().toString());
entry.append(" ");

long requestContentLength = request.getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
if (requestContentLength != -1)
{
entry.append("(Length:%,d) ".formatted(requestContentLength));
}

entry.append(response.getStatus());
entry.append(" ");

Fields queryFields = getRequestQueryFields(request);
appendFields(entry, "Query", queryFields);

entry.append(" ");

Fields formFields = getRequestFormFields(request);
appendFields(entry, "Form", formFields);

logWriter.write(entry.toString());
}
catch (Throwable e)
{
LOG.warn("Unable to log request", e);
}
}

private void appendFields(Appendable appendable, String type, Fields fields) throws IOException
{
appendable.append("[");
appendable.append(type);
if (fields == null)
{
appendable.append(": <null>]");
return;
}

appendable.append(".size=%d".formatted(fields.getSize()));
for (Fields.Field field: fields)
{
appendable.append(", %s=%s".formatted(field.getName(), field.getValue()));
}
appendable.append("]");
}

private Fields getRequestFormFields(Request request)
{
Object attr = request.getAttribute(FormFields.class.getName());
if (attr instanceof FormFields futureFormFields)
{
try
{
return futureFormFields.get();
}
catch (InterruptedException | ExecutionException e)
{
return null;
}
}
else if (attr instanceof Fields fields)
{
return fields;
}
return null;
}

private Fields getRequestQueryFields(Request request)
{
return Request.extractQueryParameters(request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package examples;

import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.server.Handler;
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.Slf4jRequestLogWriter;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Fields;

public class ParamRequestLogDemo
{
public static void main(String[] args) throws Exception
{
Server server = ParamRequestLogDemo.newServer(8080);
server.start();
server.join();
}

public static Server newServer(int port)
{
Server server = new Server(port);

Handler.Sequence handlers = new Handler.Sequence();
server.setHandler(handlers);

ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);

contexts.addHandler(new ContextHandler(new NoReadParamHandler(), "/no-read"));
contexts.addHandler(new ContextHandler(new ReadParamHandler(), "/read"));

Slf4jRequestLogWriter requestLoggingWriter = new Slf4jRequestLogWriter();
requestLoggingWriter.setLoggerName("EXAMPLE.REQUESTLOG");
RequestLog requestLog = new ParamRequestLog(requestLoggingWriter);
server.setRequestLog(requestLog);

return server;
}

public static class ReadParamHandler extends Handler.Abstract
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
Fields params = Request.getParameters(request);
StringBuilder reply = new StringBuilder("Params.size=%d%n".formatted(params.getSize()));
for (Fields.Field field: params)
{
reply.append("Param[%s]=%s%n".formatted(field.getName(), field.getValue()));
}
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
Content.Sink.write(response, true, reply.toString(), callback);
return true;
}
}

public static class NoReadParamHandler extends Handler.Abstract
{
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
long requestContentLength = request.getHeaders().getLongField(HttpHeader.CONTENT_LENGTH);
String reply = "Didn't read params, Request.header[content-length]=%d%n".formatted(requestContentLength);
response.getHeaders().put(HttpHeader.CONTENT_TYPE, "text/plain; charset=utf-8");
Content.Sink.write(response, true, reply, callback);
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// ========================================================================
// Copyright (c) 1995 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package examples;

import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.component.LifeCycle;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

public class ParamRequestLogDemoTest
{
private static final Logger LOG = LoggerFactory.getLogger(ParamRequestLogDemoTest.class);
private Server server;

@BeforeEach
public void startServer() throws Exception
{
LOG.warn("===== SEE Console Logging Output for \":INFO :EXAMPLE.REQUESTLOG:\" entries =====");

server = ParamRequestLogDemo.newServer(0);
server.start();
}

@AfterEach
public void stopServer()
{
LifeCycle.stop(server);
}

/**
* Demonstrate behavior with request that have no form sent (in request body).
*/
@ParameterizedTest
@CsvSource(delimiter = '|', textBlock = """
# Path | Expected Response Status
/read/ | 200
/read/?name=Bob | 200
/no-read/ | 200
/no-read/?name=Carl | 200
/no-read/?name=Anne&role=Chef | 200
/other/ | 404
""")
public void testRequestNoForm(String path, int expectedStatus) throws IOException, InterruptedException
{
HttpClient client = HttpClient.newBuilder().build();

HttpRequest request = HttpRequest.newBuilder()
.uri(server.getURI().resolve(path))
.version(HttpClient.Version.HTTP_1_1)
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(UTF_8));
assertThat(response.statusCode(), is(expectedStatus));
}

/**
* Demonstrate behavior with request that has a form sent (as a request body).
*/
@ParameterizedTest
@CsvSource(delimiter = '|', textBlock = """
# Path | Form Data | Expected Response Status
/read/ | co=Canada | 200
/read/?name=Bob | co=Aussie | 200
/no-read/ | co=Swiss | 200
/no-read/?name=Carl | co=Aruba | 200
/no-read/?name=Anne&role=Chef | co=Sweden | 200
/other/ | co=France | 404
""")
public void testRequestUrlForm(String path, String formdata, int expectedStatus) throws IOException, InterruptedException
{
HttpClient client = HttpClient.newBuilder().build();

HttpRequest request = HttpRequest.newBuilder()
.uri(server.getURI().resolve(path))
.version(HttpClient.Version.HTTP_1_1)
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(formdata))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString(UTF_8));
assertThat(response.statusCode(), is(expectedStatus));
}
}

0 comments on commit 48fe422

Please sign in to comment.