Skip to content

Commit

Permalink
refactoring; fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tepi committed Nov 15, 2024
1 parent 935a938 commit b65e171
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.util.Optional;

/**
* RequestHandler which takes care of locking and unlocking of the VaadinSession
Expand All @@ -30,7 +32,12 @@
*/
public abstract class SynchronizedRequestHandler implements RequestHandler {

private static final int MAX_BUFFER_SIZE = 64 * 1024;
public static final int MAX_BUFFER_SIZE = 64 * 1024;

@FunctionalInterface
public interface ResponseWriter extends Serializable {
void writeResponse() throws IOException;
}

@Override
public boolean handleRequest(VaadinSession session, VaadinRequest request,
Expand All @@ -45,8 +52,13 @@ public boolean handleRequest(VaadinSession session, VaadinRequest request,
String requestBody = reader == null ? null
: getRequestBody(reader);
session.lock();
return synchronizedHandleRequest(session, request, response,
requestBody);
Optional<ResponseWriter> responseWriter = synchronizedHandleRequest(
session, request, response, requestBody);
session.unlock();
if (responseWriter.isPresent()) {
responseWriter.get().writeResponse();
}
return responseWriter.isPresent();
} else {
session.lock();
return synchronizedHandleRequest(session, request, response);
Expand Down Expand Up @@ -109,14 +121,19 @@ public boolean isReadRequestBodyFirstEnabled() {
* The response object to which a response can be written.
* @param requestBody
* Request body pre-read from the request object
* @return true if a response has been written and no further request
* handlers should be called, otherwise false
* @return if this handler has handled the request and no further request
* handlers should be called an {@link Optional} of
* {@link ResponseWriter} is returned and it must be run after
* releasing the lock on {@link VaadinSession}. Otherwise an empty
* {@link Optional} is returned.
*
* @throws IOException
* If an IO error occurred
* @see #handleRequest(VaadinSession, VaadinRequest, VaadinResponse)
*/
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response, String requestBody)
public Optional<ResponseWriter> synchronizedHandleRequest(
VaadinSession session, VaadinRequest request,
VaadinResponse response, String requestBody)
throws IOException, UnsupportedOperationException {
throw new UnsupportedOperationException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,23 @@ private static void callErrorHandler(UI ui, JsonObject invocationJson,
ui.getSession().getErrorHandler().error(event);
}

protected String getMessage(Reader reader) throws IOException {

StringBuilder sb = new StringBuilder(
SynchronizedRequestHandler.MAX_BUFFER_SIZE);
char[] buffer = new char[SynchronizedRequestHandler.MAX_BUFFER_SIZE];

while (true) {
int read = reader.read(buffer);
if (read == -1) {
break;
}
sb.append(buffer, 0, read);
}

return sb.toString();
}

private static Logger getLogger() {
return LoggerFactory.getLogger(ServerRpcHandler.class.getName());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -104,8 +105,12 @@ public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response) throws IOException {
String requestBody = SynchronizedRequestHandler
.getRequestBody(request.getReader());
return synchronizedHandleRequest(session, request, response,
requestBody);
Optional<ResponseWriter> responseWriter = synchronizedHandleRequest(
session, request, response, requestBody);
if (responseWriter.isPresent()) {
responseWriter.get().writeResponse();
}
return responseWriter.isPresent();
}

@Override
Expand All @@ -114,16 +119,16 @@ public boolean isReadRequestBodyFirstEnabled() {
}

@Override
public boolean synchronizedHandleRequest(VaadinSession session,
VaadinRequest request, VaadinResponse response, String requestBody)
public Optional<ResponseWriter> synchronizedHandleRequest(
VaadinSession session, VaadinRequest request,
VaadinResponse response, String requestBody)
throws IOException, UnsupportedOperationException {
UI uI = session.getService().findUI(request);
if (uI == null) {
// This should not happen but it will if the UI has been closed. We
// really don't want to see it in the server logs though
commitJsonResponse(response,
VaadinService.createUINotFoundJSON(false));
return true;
return Optional.of(() -> commitJsonResponse(response,
VaadinService.createUINotFoundJSON(false)));
}

StringWriter stringWriter = new StringWriter();
Expand All @@ -134,35 +139,28 @@ public boolean synchronizedHandleRequest(VaadinSession session,
} catch (JsonException e) {
getLogger().error("Error writing JSON to response", e);
// Refresh on client side
session.unlock();
writeRefresh(response);
return true;
return Optional.of(() -> writeRefresh(response));
} catch (InvalidUIDLSecurityKeyException e) {
getLogger().warn("Invalid security key received from {}",
request.getRemoteHost());
// Refresh on client side
session.unlock();
writeRefresh(response);
return true;
return Optional.of(() -> writeRefresh(response));
} catch (DauEnforcementException e) {
getLogger().warn(
"Daily Active User limit reached. Blocking new user request");
response.setHeader(DAUUtils.STATUS_CODE_KEY, String
.valueOf(HttpStatusCode.SERVICE_UNAVAILABLE.getCode()));
String json = DAUUtils.jsonEnforcementResponse(request, e);
session.unlock();
commitJsonResponse(response, json);
return true;
return Optional.of(() -> commitJsonResponse(response, json));
} catch (ResynchronizationRequiredException e) { // NOSONAR
// Resync on the client side
writeUidl(uI, stringWriter, true);
} finally {
stringWriter.close();
}

session.unlock();
commitJsonResponse(response, stringWriter.toString());
return true;
return Optional.of(
() -> commitJsonResponse(response, stringWriter.toString()));
}

private void writeRefresh(VaadinResponse response) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.io.Reader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Optional;
import java.util.Properties;

import org.junit.Assert;
Expand All @@ -33,6 +34,7 @@
import com.vaadin.flow.server.DefaultDeploymentConfiguration;
import com.vaadin.flow.server.HandlerHelper.RequestType;
import com.vaadin.flow.server.MockVaadinContext;
import com.vaadin.flow.server.SynchronizedRequestHandler;
import com.vaadin.flow.server.VaadinContext;
import com.vaadin.flow.server.VaadinRequest;
import com.vaadin.flow.server.VaadinResponse;
Expand Down Expand Up @@ -112,10 +114,11 @@ public void writeSessionExpired_whenUINotFound() throws IOException {

when(service.findUI(request)).thenReturn(null);

boolean result = handler.synchronizedHandleRequest(session, request,
response, null);
Assert.assertTrue("Result should be true", result);

Optional<SynchronizedRequestHandler.ResponseWriter> result = handler
.synchronizedHandleRequest(session, request, response, null);
Assert.assertTrue("ResponseWriter should be present",
result.isPresent());
result.get().writeResponse();
String responseContent = CommunicationUtil
.getStringWhenWriteString(outputStream);

Expand Down Expand Up @@ -229,37 +232,36 @@ public void should_not_update_browser_history_if_no_hash_in_location()
Assert.assertFalse(out.contains("history.pushState"));
}

// TODO Teppo: Fix test
// @Test
// public void
// synchronizedHandleRequest_DauEnforcementException_setsStatusCode503()
// throws IOException {
// VaadinService service = mock(VaadinService.class);
// VaadinSession session = mock(VaadinSession.class);
// when(session.getService()).thenReturn(service);
// UI ui = Mockito.mock(UI.class);
//
// when(service.findUI(request)).thenReturn(ui);
//
// ServerRpcHandler serverRpcHandler = new ServerRpcHandler() {
// @Override
// public void handleRpc(UI ui, Reader reader, VaadinRequest request) {
// throw new DauEnforcementException(
// new EnforcementException("test"));
// }
// };
//
// UidlRequestHandler handler = new UidlRequestHandler() {
// @Override
// protected ServerRpcHandler createRpcHandler() {
// return serverRpcHandler;
// }
// };
//
// handler.synchronizedHandleRequest(session, request, response);
//
// Mockito.verify(response).setHeader(DAUUtils.STATUS_CODE_KEY, "503");
// }
@Test
public void synchronizedHandleRequest_DauEnforcementException_setsStatusCode503()
throws IOException {
VaadinService service = mock(VaadinService.class);
VaadinSession session = mock(VaadinSession.class);
when(session.getService()).thenReturn(service);
UI ui = Mockito.mock(UI.class);

when(service.findUI(request)).thenReturn(ui);

ServerRpcHandler serverRpcHandler = new ServerRpcHandler() {
@Override
public void handleRpc(UI ui, String requestBody,
VaadinRequest request) {
throw new DauEnforcementException(
new EnforcementException("test"));
}
};

UidlRequestHandler handler = new UidlRequestHandler() {
@Override
protected ServerRpcHandler createRpcHandler() {
return serverRpcHandler;
}
};

handler.synchronizedHandleRequest(session, request, response, "");

Mockito.verify(response).setHeader(DAUUtils.STATUS_CODE_KEY, "503");
}

private JsonObject generateUidl(boolean withLocation, boolean withHash) {

Expand Down

0 comments on commit b65e171

Please sign in to comment.