Skip to content

Commit be4face

Browse files
committed
Relax check on default data MimeType
If there is more than one non-basic codec (e.g. CBOR and JSON) RSocketRequester.Builder takes the mime type of the first one rather than giving up. It is a valid scenario (JSON for server responding to browser, and CBOR for client talking to server) and it is the default situation in Boot, and after all the point here is to pick some default as best as we can with the worst possible outcome being a server refusing the connection if it doesn't support the mime type. Beyond that applications can set the dataMimeType on the builder explicitly. To match that change this commit also ensures RSocketMessageHandler rejects proactively data mime types it does not support at the point of accepting a connection.
1 parent 88016d4 commit be4face

File tree

4 files changed

+30
-38
lines changed

4 files changed

+30
-38
lines changed

spring-messaging/src/main/java/org/springframework/messaging/rsocket/DefaultRSocketRequesterBuilder.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,21 +145,14 @@ private MimeType getDataMimeType(RSocketStrategies strategies) {
145145
if (this.dataMimeType != null) {
146146
return this.dataMimeType;
147147
}
148-
// Look for non-basic Decoder (e.g. CBOR, Protobuf)
149-
MimeType selected = null;
150-
List<Decoder<?>> decoders = strategies.decoders();
151-
for (Decoder<?> candidate : decoders) {
148+
// First non-basic Decoder (e.g. CBOR, Protobuf)
149+
for (Decoder<?> candidate : strategies.decoders()) {
152150
if (!isCoreCodec(candidate) && !candidate.getDecodableMimeTypes().isEmpty()) {
153-
Assert.state(selected == null,
154-
() -> "Cannot select default data MimeType based on configured decoders: " + decoders);
155-
selected = getMimeType(candidate);
151+
return getMimeType(candidate);
156152
}
157153
}
158-
if (selected != null) {
159-
return selected;
160-
}
161-
// Fall back on 1st decoder (e.g. String)
162-
for (Decoder<?> decoder : decoders) {
154+
// First core decoder (e.g. String)
155+
for (Decoder<?> decoder : strategies.decoders()) {
163156
if (!decoder.getDecodableMimeTypes().isEmpty()) {
164157
return getMimeType(decoder);
165158
}

spring-messaging/src/main/java/org/springframework/messaging/rsocket/RSocketRequester.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,10 @@ interface Builder {
128128
/**
129129
* Configure the payload data MimeType to specify on the {@code SETUP}
130130
* frame that applies to the whole connection.
131-
* <p>If this is not set, the builder will try to select the mime type
132-
* based on the presence of a single
131+
* <p>If this is not set, it will be set to the MimeType of the first
133132
* {@link RSocketStrategies.Builder#decoder(Decoder[]) non-default}
134-
* {@code Decoder}, or the first default decoder otherwise
135-
* (i.e. {@code String}) if no others are configured.
133+
* {@code Decoder}, or otherwise fall back on the MimeType of the first
134+
* (default) decoder.
136135
*/
137136
RSocketRequester.Builder dataMimeType(@Nullable MimeType mimeType);
138137

spring-messaging/src/main/java/org/springframework/messaging/rsocket/annotation/support/RSocketMessageHandler.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,13 @@ protected void handleNoMatch(@Nullable RouteMatcher.Route destination, Message<?
304304
*/
305305
public SocketAcceptor serverResponder() {
306306
return (setupPayload, sendingRSocket) -> {
307-
MessagingRSocket responder = createResponder(setupPayload, sendingRSocket);
307+
MessagingRSocket responder;
308+
try {
309+
responder = createResponder(setupPayload, sendingRSocket);
310+
}
311+
catch (Throwable ex) {
312+
return Mono.error(ex);
313+
}
308314
return responder.handleConnectionSetupPayload(setupPayload).then(Mono.just(responder));
309315
};
310316
}
@@ -335,25 +341,36 @@ private MessagingRSocket createResponder(ConnectionSetupPayload setupPayload, RS
335341
String s = setupPayload.dataMimeType();
336342
MimeType dataMimeType = StringUtils.hasText(s) ? MimeTypeUtils.parseMimeType(s) : this.defaultDataMimeType;
337343
Assert.notNull(dataMimeType, "No `dataMimeType` in ConnectionSetupPayload and no default value");
344+
Assert.isTrue(isDataMimeTypeSupported(dataMimeType), "Data MimeType '" + dataMimeType + "' not supported");
338345

339346
s = setupPayload.metadataMimeType();
340-
MimeType metadataMimeType = StringUtils.hasText(s) ? MimeTypeUtils.parseMimeType(s) : this.defaultMetadataMimeType;
341-
Assert.notNull(metadataMimeType, "No `metadataMimeType` in ConnectionSetupPayload and no default value");
347+
MimeType metaMimeType = StringUtils.hasText(s) ? MimeTypeUtils.parseMimeType(s) : this.defaultMetadataMimeType;
348+
Assert.notNull(metaMimeType, "No `metadataMimeType` in ConnectionSetupPayload and no default value");
342349

343350
RSocketStrategies strategies = this.rsocketStrategies;
344351
Assert.notNull(strategies, "No RSocketStrategies. Was afterPropertiesSet not called?");
345-
RSocketRequester requester = RSocketRequester.wrap(rsocket, dataMimeType, metadataMimeType, strategies);
352+
RSocketRequester requester = RSocketRequester.wrap(rsocket, dataMimeType, metaMimeType, strategies);
346353

347354
Assert.state(this.metadataExtractor != null,
348355
() -> "No MetadataExtractor. Was afterPropertiesSet not called?");
349356

350357
Assert.state(getRouteMatcher() != null,
351358
() -> "No RouteMatcher. Was afterPropertiesSet not called?");
352359

353-
return new MessagingRSocket(dataMimeType, metadataMimeType, this.metadataExtractor, requester,
360+
return new MessagingRSocket(dataMimeType, metaMimeType, this.metadataExtractor, requester,
354361
this, getRouteMatcher(), strategies);
355362
}
356363

364+
private boolean isDataMimeTypeSupported(MimeType dataMimeType) {
365+
for (Encoder<?> encoder : getEncoders()) {
366+
for (MimeType encodable : encoder.getEncodableMimeTypes()) {
367+
if (encodable.isCompatibleWith(dataMimeType)) {
368+
return true;
369+
}
370+
}
371+
}
372+
return false;
373+
}
357374

358375
public static ClientRSocketFactoryConfigurer clientResponder(Object... handlers) {
359376
return new ResponderConfigurer(handlers);

spring-messaging/src/test/java/org/springframework/messaging/rsocket/DefaultRSocketRequesterBuilderTests.java

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import org.springframework.util.ReflectionUtils;
4747

4848
import static org.assertj.core.api.Assertions.assertThat;
49-
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5049
import static org.mockito.ArgumentMatchers.any;
5150
import static org.mockito.ArgumentMatchers.anyInt;
5251
import static org.mockito.BDDMockito.given;
@@ -131,22 +130,6 @@ public void defaultDataMimeTypeWithCustomDecoderRegitered() {
131130
.isEqualTo(MimeTypeUtils.APPLICATION_JSON);
132131
}
133132

134-
@Test
135-
public void defaultDataMimeTypeWithMultipleCustomDecoderRegitered() {
136-
RSocketStrategies strategies = RSocketStrategies.builder()
137-
.decoder(new TestJsonDecoder(MimeTypeUtils.APPLICATION_JSON))
138-
.decoder(new TestJsonDecoder(MimeTypeUtils.APPLICATION_XML))
139-
.build();
140-
141-
assertThatThrownBy(() ->
142-
RSocketRequester
143-
.builder()
144-
.rsocketStrategies(strategies)
145-
.connect(this.transport)
146-
.block())
147-
.hasMessageContaining("Cannot select default data MimeType");
148-
}
149-
150133
@Test
151134
public void dataMimeTypeSet() {
152135
RSocketRequester requester = RSocketRequester.builder()

0 commit comments

Comments
 (0)