|
|
@ -28,6 +28,8 @@ import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.concurrent.atomic.AtomicBoolean; |
|
|
|
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.Flux; |
|
|
|
import reactor.core.publisher.Mono; |
|
|
|
import reactor.core.publisher.Mono; |
|
|
|
|
|
|
|
|
|
|
@ -60,6 +62,8 @@ import org.springframework.util.StringUtils; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class DefaultMultipartMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> { |
|
|
|
public class DefaultMultipartMessageReader extends LoggingCodecSupport implements HttpMessageReader<Part> { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final Log logger = LogFactory.getLog(DefaultMultipartMessageReader.class); |
|
|
|
|
|
|
|
|
|
|
|
private static final byte CR = '\r'; |
|
|
|
private static final byte CR = '\r'; |
|
|
|
|
|
|
|
|
|
|
|
private static final byte LF = '\n'; |
|
|
|
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: \"" + |
|
|
|
return Flux.error(new CodecException("No multipart boundary found in Content-Type: \"" + |
|
|
|
message.getHeaders().getContentType() + "\"")); |
|
|
|
message.getHeaders().getContentType() + "\"")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
|
|
|
|
logger.trace("Boundary: " + toString(boundary)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
byte[] boundaryNeedle = concat(BOUNDARY_PREFIX, boundary); |
|
|
|
byte[] boundaryNeedle = concat(BOUNDARY_PREFIX, boundary); |
|
|
|
Flux<DataBuffer> body = skipUntilFirstBoundary(message.getBody(), boundary); |
|
|
|
Flux<DataBuffer> body = skipUntilFirstBoundary(message.getBody(), boundary); |
|
|
@ -139,6 +146,9 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement |
|
|
|
int length = dataBuffer.writePosition() - 1 - endIdx; |
|
|
|
int length = dataBuffer.writePosition() - 1 - endIdx; |
|
|
|
DataBuffer slice = dataBuffer.retainedSlice(endIdx + 1, length); |
|
|
|
DataBuffer slice = dataBuffer.retainedSlice(endIdx + 1, length); |
|
|
|
DataBufferUtils.release(dataBuffer); |
|
|
|
DataBufferUtils.release(dataBuffer); |
|
|
|
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
|
|
|
|
logger.trace("Found first boundary at " + endIdx + " in " + toString(dataBuffer)); |
|
|
|
|
|
|
|
} |
|
|
|
return Mono.just(slice); |
|
|
|
return Mono.just(slice); |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
@ -176,6 +186,10 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement |
|
|
|
dataBuffer.readPosition(readPosition + 2); |
|
|
|
dataBuffer.readPosition(readPosition + 2); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (logger.isTraceEnabled()) { |
|
|
|
|
|
|
|
logger.trace("Part data: " + toString(dataBuffer)); |
|
|
|
|
|
|
|
} |
|
|
|
int endIdx = HEADER_MATCHER.match(dataBuffer); |
|
|
|
int endIdx = HEADER_MATCHER.match(dataBuffer); |
|
|
|
|
|
|
|
|
|
|
|
HttpHeaders headers; |
|
|
|
HttpHeaders headers; |
|
|
@ -237,6 +251,33 @@ public class DefaultMultipartMessageReader extends LoggingCodecSupport implement |
|
|
|
return result; |
|
|
|
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 |
|
|
|
@Override |
|
|
|
public Mono<Part> readMono(ResolvableType elementType, ReactiveHttpInputMessage message, |
|
|
|
public Mono<Part> readMono(ResolvableType elementType, ReactiveHttpInputMessage message, |
|
|
|
Map<String, Object> hints) { |
|
|
|
Map<String, Object> hints) { |
|
|
|