|
|
|
@ -1,5 +1,5 @@ |
|
|
|
/* |
|
|
|
/* |
|
|
|
* Copyright 2002-2019 the original author or authors. |
|
|
|
* Copyright 2002-2020 the original author or authors. |
|
|
|
* |
|
|
|
* |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
* you may not use this file except in compliance with the License. |
|
|
|
@ -18,7 +18,7 @@ package org.springframework.http.codec; |
|
|
|
|
|
|
|
|
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.nio.charset.StandardCharsets; |
|
|
|
import java.time.Duration; |
|
|
|
import java.time.Duration; |
|
|
|
import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
@ -112,7 +112,7 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Flux<Publisher<DataBuffer>> encode(Publisher<?> input, ResolvableType elementType, |
|
|
|
private Flux<Publisher<DataBuffer>> encode(Publisher<?> input, ResolvableType elementType, |
|
|
|
MediaType mediaType, DataBufferFactory bufferFactory, Map<String, Object> hints) { |
|
|
|
MediaType mediaType, DataBufferFactory factory, Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
ResolvableType dataType = (ServerSentEvent.class.isAssignableFrom(elementType.toClass()) ? |
|
|
|
ResolvableType dataType = (ServerSentEvent.class.isAssignableFrom(elementType.toClass()) ? |
|
|
|
elementType.getGeneric() : elementType); |
|
|
|
elementType.getGeneric() : elementType); |
|
|
|
@ -144,13 +144,35 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec |
|
|
|
sb.append("data:"); |
|
|
|
sb.append("data:"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
Mono<DataBuffer> bufferMono = Mono.fromCallable(() -> |
|
|
|
Flux<DataBuffer> result; |
|
|
|
bufferFactory.join(encodeEvent(sb, data, dataType, mediaType, bufferFactory, hints))); |
|
|
|
if (data == null) { |
|
|
|
|
|
|
|
result = Flux.just(encodeText(sb + "\n", mediaType, factory)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (data instanceof String) { |
|
|
|
|
|
|
|
data = StringUtils.replace((String) data, "\n", "\ndata:"); |
|
|
|
|
|
|
|
result = Flux.just(encodeText(sb + (String) data + "\n\n", mediaType, factory)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
result = encodeEvent(sb.toString(), data, dataType, mediaType, factory, hints); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return bufferMono.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); |
|
|
|
return result.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
|
|
|
private <T> Flux<DataBuffer> encodeEvent(String eventContent, T data, ResolvableType dataType, |
|
|
|
|
|
|
|
MediaType mediaType, DataBufferFactory factory, Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.encoder == null) { |
|
|
|
|
|
|
|
throw new CodecException("No SSE encoder configured and the data is not String."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return Flux.just(factory.join(Arrays.asList( |
|
|
|
|
|
|
|
encodeText(eventContent, mediaType, factory), |
|
|
|
|
|
|
|
((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints), |
|
|
|
|
|
|
|
encodeText("\n\n", mediaType, factory)))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void writeField(String fieldName, Object fieldValue, StringBuilder sb) { |
|
|
|
private void writeField(String fieldName, Object fieldValue, StringBuilder sb) { |
|
|
|
sb.append(fieldName); |
|
|
|
sb.append(fieldName); |
|
|
|
sb.append(':'); |
|
|
|
sb.append(':'); |
|
|
|
@ -158,29 +180,6 @@ public class ServerSentEventHttpMessageWriter implements HttpMessageWriter<Objec |
|
|
|
sb.append("\n"); |
|
|
|
sb.append("\n"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
|
|
|
|
|
|
private <T> List<DataBuffer> encodeEvent(CharSequence markup, @Nullable T data, ResolvableType dataType, |
|
|
|
|
|
|
|
MediaType mediaType, DataBufferFactory factory, Map<String, Object> hints) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<DataBuffer> result = new ArrayList<>(4); |
|
|
|
|
|
|
|
result.add(encodeText(markup, mediaType, factory)); |
|
|
|
|
|
|
|
if (data != null) { |
|
|
|
|
|
|
|
if (data instanceof String) { |
|
|
|
|
|
|
|
String dataLine = StringUtils.replace((String) data, "\n", "\ndata:") + "\n"; |
|
|
|
|
|
|
|
result.add(encodeText(dataLine, mediaType, factory)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else if (this.encoder == null) { |
|
|
|
|
|
|
|
throw new CodecException("No SSE encoder configured and the data is not String."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
result.add(((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints)); |
|
|
|
|
|
|
|
result.add(encodeText("\n", mediaType, factory)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
result.add(encodeText("\n", mediaType, factory)); |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private DataBuffer encodeText(CharSequence text, MediaType mediaType, DataBufferFactory bufferFactory) { |
|
|
|
private DataBuffer encodeText(CharSequence text, MediaType mediaType, DataBufferFactory bufferFactory) { |
|
|
|
Assert.notNull(mediaType.getCharset(), "Expected MediaType with charset"); |
|
|
|
Assert.notNull(mediaType.getCharset(), "Expected MediaType with charset"); |
|
|
|
byte[] bytes = text.toString().getBytes(mediaType.getCharset()); |
|
|
|
byte[] bytes = text.toString().getBytes(mediaType.getCharset()); |
|
|
|
|