Skip to content

Commit

Permalink
Fix binding mixed attributes to body parameters (#6764)
Browse files Browse the repository at this point in the history
`NettyHttpRequest.buildBody` distinguishes between requests with "received data" and "received content", where the former only applies when `AbstractHttpData` instances are present. `AbstractHttpData` is a base class of both `DiskAttribute` and `MemoryAttribute`, but not of `MixedAttribute`. This makes `buildBody` use the "content" path for mixed requests.

This change adds an exception for `MixedAttribute` so that it is also covered by the "data" path.

Fixes #6705
  • Loading branch information
yawkat committed Jan 18, 2022
1 parent b022323 commit d8cb3e7
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
Expand Up @@ -61,6 +61,8 @@
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
import io.netty.handler.codec.http.multipart.AbstractHttpData;
import io.netty.handler.codec.http.multipart.HttpData;
import io.netty.handler.codec.http.multipart.MixedAttribute;
import io.netty.handler.codec.http2.Http2ConnectionHandler;
import io.netty.handler.codec.http2.HttpConversionUtil;
import io.netty.handler.ssl.SslHandler;
Expand Down Expand Up @@ -144,7 +146,7 @@ public class NettyHttpRequest<T> extends AbstractNettyHttpRequest<T> implements
private MutableConvertibleValues<Object> attributes;
private NettyCookies nettyCookies;
private List<ByteBufHolder> receivedContent = new ArrayList<>();
private Map<IdentityWrapper, AbstractHttpData> receivedData = new LinkedHashMap<>();
private Map<IdentityWrapper, HttpData> receivedData = new LinkedHashMap<>();

private Supplier<Optional<T>> body;
private RouteMatch<?> matchedRoute;
Expand Down Expand Up @@ -297,7 +299,7 @@ protected Object buildBody() {
if (!receivedData.isEmpty()) {
Map body = new LinkedHashMap(receivedData.size());

for (AbstractHttpData data: receivedData.values()) {
for (HttpData data: receivedData.values()) {
String newValue = getContent(data);
//noinspection unchecked
body.compute(data.getName(), (key, oldValue) -> {
Expand Down Expand Up @@ -332,7 +334,7 @@ protected Object buildBody() {
}
}

private String getContent(AbstractHttpData data) {
private String getContent(HttpData data) {
String newValue;
try {
newValue = data.getString(serverConfiguration.getDefaultCharset());
Expand Down Expand Up @@ -412,10 +414,10 @@ public RouteMatch<?> getMatchedRoute() {
*/
@Internal
public void addContent(ByteBufHolder httpContent) {
if (httpContent instanceof AbstractHttpData) {
if (httpContent instanceof AbstractHttpData || httpContent instanceof MixedAttribute) {
receivedData.computeIfAbsent(new IdentityWrapper(httpContent), key -> {
httpContent.retain();
return (AbstractHttpData) httpContent;
return (HttpData) httpContent;
});
} else {
receivedContent.add(httpContent.retain());
Expand Down
Expand Up @@ -430,6 +430,28 @@ class MixedUploadSpec extends AbstractMicronautSpec {
result == 'data.json: 16'
}

void "test normal form items"() {
given:
MultipartBody requestBody = MultipartBody.builder()
.addPart("title", "foo")
.build()


when:
Mono<HttpResponse<String>> flowable = Mono.from(client.exchange(
HttpRequest.POST("/upload/receive-multipart", requestBody)
.contentType(MediaType.MULTIPART_FORM_DATA_TYPE)
.accept(MediaType.TEXT_PLAIN_TYPE),
String
))
HttpResponse<String> response = flowable.block()
def result = response.getBody().get()

then:
response.code() == HttpStatus.OK.code
result == "Data{title='foo'}"
}

@Override
Map<String, Object> getConfiguration() {
super.getConfiguration() << ['micronaut.http.client.read-timeout': 300,
Expand Down
Expand Up @@ -75,6 +75,11 @@ public String receiveBytes(byte[] data, String title) {
return title + ": " + data.length;
}

@Post(value = "/receive-multipart", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN)
public String receiveMultipart(@Body Data data) {
return data.toString();
}

@Post(value = "/receive-file-upload", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN)
public Publisher<MutableHttpResponse<?>> receiveFileUpload(StreamingFileUpload data, String title) {
long size = data.getDefinedSize();
Expand Down

0 comments on commit d8cb3e7

Please sign in to comment.