diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java index 8a4c78d165..d24ebe7d17 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultMultipartMessageReader.java @@ -28,6 +28,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -60,6 +62,8 @@ import org.springframework.util.StringUtils; */ public class DefaultMultipartMessageReader extends LoggingCodecSupport implements HttpMessageReader { + private static final Log logger = LogFactory.getLog(DefaultMultipartMessageReader.class); + private static final byte CR = '\r'; private static final byte LF = '\n'; @@ -95,6 +99,9 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement return Flux.error(new CodecException("No multipart boundary found in Content-Type: \"" + message.getHeaders().getContentType() + "\"")); } + if (logger.isTraceEnabled()) { + logger.trace("Boundary: " + toString(boundary)); + } byte[] boundaryNeedle = concat(BOUNDARY_PREFIX, boundary); Flux body = skipUntilFirstBoundary(message.getBody(), boundary); @@ -139,6 +146,9 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement int length = dataBuffer.writePosition() - 1 - endIdx; DataBuffer slice = dataBuffer.retainedSlice(endIdx + 1, length); DataBufferUtils.release(dataBuffer); + if (logger.isTraceEnabled()) { + logger.trace("Found first boundary at " + endIdx + " in " + toString(dataBuffer)); + } return Mono.just(slice); } else { @@ -176,6 +186,10 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement dataBuffer.readPosition(readPosition + 2); } } + + if (logger.isTraceEnabled()) { + logger.trace("Part data: " + toString(dataBuffer)); + } int endIdx = HEADER_MATCHER.match(dataBuffer); HttpHeaders headers; @@ -237,6 +251,33 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement return result; } + + private static String toString(DataBuffer dataBuffer) { + byte[] bytes = new byte[dataBuffer.readableByteCount()]; + int j = 0; + for (int i = dataBuffer.readPosition(); i < dataBuffer.writePosition(); i++) { + bytes[j++] = dataBuffer.getByte(i); + } + return toString(bytes); + } + + private static String toString(byte[] bytes) { + StringBuilder builder = new StringBuilder(); + for (byte b : bytes) { + if (b == CR) { + builder.append("␍"); + } + else if (b == LF) { + builder.append("␤"); + } + else if (b >= 20 && b <= 126) { + builder.append((char) b); + } + } + return builder.toString(); + } + + @Override public Mono readMono(ResolvableType elementType, ReactiveHttpInputMessage message, Map hints) {