/** * Create a {@code StringDecoder} that supports all MIME types. */ public static StringDecoder allMimeTypes() { return allMimeTypes(DEFAULT_DELIMITERS, true); }
/** * Create a {@code StringDecoder} for {@code "text/plain"}. */ public static StringDecoder textPlainOnly() { return textPlainOnly(DEFAULT_DELIMITERS, true); }
/** * Create a {@code StringDecoder} for {@code "text/plain"}. * @param delimiters delimiter strings to use to split the input stream * @param stripDelimiter whether to remove delimiters from the resulting * input strings */ public static StringDecoder textPlainOnly(List<String> delimiters, boolean stripDelimiter) { return new StringDecoder(delimiters, stripDelimiter, new MimeType("text", "plain", DEFAULT_CHARSET)); }
@Override public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { List<byte[]> delimiterBytes = getDelimiterBytes(mimeType); Flux<DataBuffer> inputFlux = Flux.from(inputStream) .flatMapIterable(dataBuffer -> splitOnDelimiter(dataBuffer, delimiterBytes)) .bufferUntil(StringDecoder::isEndFrame) .map(StringDecoder::joinUntilEndFrame) .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); return super.decode(inputFlux, elementType, mimeType, hints); }
@Override protected void register(GenericApplicationContext context, CodecConfigurer configurer) { configurer.customCodecs().encoder(textPlainOnly ? CharSequenceEncoder.textPlainOnly() : CharSequenceEncoder.allMimeTypes()); configurer.customCodecs().decoder(textPlainOnly ? StringDecoder.textPlainOnly() : StringDecoder.allMimeTypes()); } }
assertEquals(MediaType.APPLICATION_JSON_UTF8, part.headers().getContentType()); String value = StringDecoder.textPlainOnly(false).decodeToMono(part.content(), ResolvableType.forClass(String.class), MediaType.TEXT_PLAIN, Collections.emptyMap()).block(Duration.ZERO); assertEquals("publisher", part.name()); value = StringDecoder.textPlainOnly(false).decodeToMono(part.content(), ResolvableType.forClass(String.class), MediaType.TEXT_PLAIN, Collections.emptyMap()).block(Duration.ZERO);
@Override public Flux<Object> read( ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { boolean shouldWrap = isServerSentEvent(elementType); ResolvableType valueType = (shouldWrap ? elementType.getGeneric() : elementType); return stringDecoder.decode(message.getBody(), STRING_TYPE, null, hints) .bufferUntil(line -> line.equals("")) .concatMap(lines -> buildEvent(lines, valueType, shouldWrap, hints)); }
private List<byte[]> getDelimiterBytes(@Nullable MimeType mimeType) { return this.delimitersCache.computeIfAbsent(getCharset(mimeType), charset -> this.delimiters.stream() .map(s -> s.getBytes(charset)) .collect(Collectors.toList())); }
@Override public Mono<Object> readMono( ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { // We're ahead of String + "*/*" // Let's see if we can aggregate the output (lest we time out)... if (elementType.resolve() == String.class) { Flux<DataBuffer> body = message.getBody(); return stringDecoder.decodeToMono(body, elementType, null, null).cast(Object.class); } return Mono.error(new UnsupportedOperationException( "ServerSentEventHttpMessageReader only supports reading stream of events as a Flux")); }
byte[] matchingDelimiter = null; for (byte[] delimiter : delimiterBytes) { int index = indexOf(dataBuffer, delimiter); if (index >= 0 && index < length) { length = index;
@Override public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { List<byte[]> delimiterBytes = getDelimiterBytes(mimeType); Flux<DataBuffer> inputFlux = Flux.from(inputStream) .flatMapIterable(dataBuffer -> splitOnDelimiter(dataBuffer, delimiterBytes)) .bufferUntil(StringDecoder::isEndFrame) .map(StringDecoder::joinUntilEndFrame) .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); return super.decode(inputFlux, elementType, mimeType, hints); }
@Override public Flux<Object> read( ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { boolean shouldWrap = isServerSentEvent(elementType); ResolvableType valueType = (shouldWrap ? elementType.getGeneric() : elementType); return stringDecoder.decode(message.getBody(), STRING_TYPE, null, hints) .bufferUntil(line -> line.equals("")) .concatMap(lines -> buildEvent(lines, valueType, shouldWrap, hints)); }
private List<byte[]> getDelimiterBytes(@Nullable MimeType mimeType) { return this.delimitersCache.computeIfAbsent(getCharset(mimeType), charset -> this.delimiters.stream() .map(s -> s.getBytes(charset)) .collect(Collectors.toList())); }
@Override public Mono<Object> readMono( ResolvableType elementType, ReactiveHttpInputMessage message, Map<String, Object> hints) { // We're ahead of String + "*/*" // Let's see if we can aggregate the output (lest we time out)... if (elementType.resolve() == String.class) { Flux<DataBuffer> body = message.getBody(); return stringDecoder.decodeToMono(body, elementType, null, null).cast(Object.class); } return Mono.error(new UnsupportedOperationException( "ServerSentEventHttpMessageReader only supports reading stream of events as a Flux")); }
byte[] matchingDelimiter = null; for (byte[] delimiter : delimiterBytes) { int index = indexOf(dataBuffer, delimiter); if (index >= 0 && index < length) { length = index;
/** * Create a {@code StringDecoder} that supports all MIME types. * @param ignored ignored * @deprecated as of Spring 5.0.4, in favor of {@link #allMimeTypes()} or * {@link #allMimeTypes(List, boolean)} */ @Deprecated public static StringDecoder allMimeTypes(boolean ignored) { return allMimeTypes(); }
/** * Create a {@code StringDecoder} for {@code "text/plain"}. * @param ignored ignored * @deprecated as of Spring 5.0.4, in favor of {@link #textPlainOnly()} or * {@link #textPlainOnly(List, boolean)} */ @Deprecated public static StringDecoder textPlainOnly(boolean ignored) { return textPlainOnly(); }
/** * Create a {@code StringDecoder} that supports all MIME types. * @param delimiters delimiter strings to use to split the input stream * @param stripDelimiter whether to remove delimiters from the resulting * input strings */ public static StringDecoder allMimeTypes(List<String> delimiters, boolean stripDelimiter) { return new StringDecoder(delimiters, stripDelimiter, new MimeType("text", "plain", DEFAULT_CHARSET), MimeTypeUtils.ALL); }
@Override public Flux<String> decode(Publisher<DataBuffer> inputStream, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { List<byte[]> delimiterBytes = getDelimiterBytes(mimeType); Flux<DataBuffer> inputFlux = Flux.from(inputStream) .flatMapIterable(dataBuffer -> splitOnDelimiter(dataBuffer, delimiterBytes)) .bufferUntil(StringDecoder::isEndFrame) .map(StringDecoder::joinUntilEndFrame) .doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release); return super.decode(inputFlux, elementType, mimeType, hints); }
@Override protected void testDecodeError(Publisher<DataBuffer> input, ResolvableType outputType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) { input = Flux.concat( Flux.from(input).take(1), Flux.error(new InputException())); Flux<String> result = this.decoder.decode(input, outputType, mimeType, hints); StepVerifier.create(result) .expectError(InputException.class) .verify(); }