diff --git a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java index df94d1617f1..b0b13aa205d 100644 --- a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java +++ b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -331,6 +331,7 @@ public final class ContentDisposition { do { int nextIndex = index + 1; boolean quoted = false; + boolean escaped = false; while (nextIndex < headerValue.length()) { char ch = headerValue.charAt(nextIndex); if (ch == ';') { @@ -338,9 +339,10 @@ public final class ContentDisposition { break; } } - else if (ch == '"') { + else if (!escaped && ch == '"') { quoted = !quoted; } + escaped = (!escaped && ch == '\\'); nextIndex++; } String part = headerValue.substring(index + 1, nextIndex).trim(); diff --git a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java index 40b04da7fe8..cef4b674f4f 100644 --- a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java +++ b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java @@ -81,6 +81,14 @@ public class ContentDispositionTests { .filename("中文.txt", StandardCharsets.UTF_8).build()); } + @Test // gh-23077 + public void parseWithEscapedQuote() { + ContentDisposition disposition = ContentDisposition.parse( + "form-data; name=\"file\"; filename=\"\\\"The Twilight Zone\\\".txt\"; size=123"); + assertThat(ContentDisposition.builder("form-data").name("file") + .filename("\\\"The Twilight Zone\\\".txt").size(123L).build()).isEqualTo(disposition); + } + @Test public void parseEmpty() { assertThatIllegalArgumentException().isThrownBy(() -> diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java index d56381a3ac7..4e190f84765 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/messaging/StompSubProtocolHandler.java @@ -338,12 +338,10 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE sendErrorMessage(session, ex); return; } - Message message = getErrorHandler().handleClientMessageProcessingError(clientMessage, ex); if (message == null) { return; } - StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); Assert.state(accessor != null, "No StompHeaderAccessor"); sendToClient(session, accessor, message.getPayload()); @@ -366,6 +364,14 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE // Could be part of normal workflow (e.g. browser tab closed) logger.debug("Failed to send STOMP ERROR to client", ex); } + finally { + try { + session.close(CloseStatus.PROTOCOL_ERROR); + } + catch (IOException ex) { + // Ignore + } + } } private boolean detectImmutableMessageInterceptor(MessageChannel channel) {