Spring 6 sending PartEvent from WebClient in IntegrationTest throws ServerWebInputException - spring

Spring 6 introduce PartEvent to handle multiparts, my example is based on Spring 6 RC2/Java 17/Maven, check the example codes.
The server side is like this.
#PostMapping("partevents")
public ResponseEntity<Flux<Object>> handlePartsEvents(#RequestBody Flux<PartEvent> allPartsEvents) {
var result = allPartsEvents
.windowUntil(PartEvent::isLast)
.concatMap(p ->
p.switchOnFirst(
(signal, partEvents) -> {
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) {
String value = formEvent.value();
// handle form field
log.debug("form value: {}", value);
return Mono.just(value);
} else if (event instanceof FilePartEvent fileEvent) {
String filename = fileEvent.filename();
log.debug("upload file name:{}", filename);
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
// handle file upload
var fileBytes = DataBufferUtils.join(contents)
.map(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
return bytes;
});
return Mono.just(filename);
} else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
return partEvents; // either complete or error signal
}
)
);
return ok().body(result);
}
In the integration test, I used a WebClient to send PartEvent to the server like this.
#Test
public void testPartEvents() throws Exception {
this.client
.post().uri("/partevents")
.contentType(MULTIPART_FORM_DATA)
.body(
Flux.concat(
FormPartEvent.create("name", "test"),
FilePartEvent.create("file", new ClassPathResource("spring.png"))
),
PartEvent.class
)
.exchangeToFlux(clientResponse -> {
assertThat(clientResponse.statusCode()).isEqualTo(HttpStatus.OK);
return clientResponse.bodyToFlux(String.class);
}
)
.as(StepVerifier::create)
.expectNextCount(2)
.verifyComplete();
}
When running the tests, and I got the following errors.
2022-10-23 21:56:28,530 ERROR [reactor-http-nio-2] org.springframework.web.server.adapter.HttpWebHandlerAdapter: [a48ba684-1] Error [org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Failed to read HTTP message"] for HTTP POST "/partevents", but ServerHttpResponse already committed (200 OK)
2022-10-23 21:56:28,532 ERROR [reactor-http-nio-2] reactor.util.Loggers$Slf4JLogger: [a48ba684-1, L:/127.0.0.1:8080 - R:/127.0.0.1:57289] Error finishing response. Closing connection
org.springframework.web.server.ServerWebInputException: 400 BAD_REQUEST "Failed to read HTTP message"
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.handleReadError(AbstractMessageReaderArgumentResolver.java:224)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Handler com.example.demo.web.FileUploadController#handlePartsEvents(Flux) [DispatcherHandler]
*__checkpoint ⇢ HTTP POST "/partevents" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.handleReadError(AbstractMessageReaderArgumentResolver.java:224)
at org.springframework.web.reactive.result.method.annotation.AbstractMessageReaderArgumentResolver.lambda$readBody$0(AbstractMessageReaderArgumentResolver.java:173)
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94)
at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.maybeOnError(FluxConcatMapNoPrefetch.java:326)
at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerError(FluxConcatMapNoPrefetch.java:297)
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onError(FluxConcatMap.java:875)
at reactor.core.publisher.FluxSwitchOnFirst$SwitchOnFirstControlSubscriber.onError(FluxSwitchOnFirst.java:955)
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onError(Operators.java:2208)
at reactor.core.publisher.FluxMap$MapSubscriber.onError(FluxMap.java:134)
at reactor.core.publisher.FluxMap$MapSubscriber.onError(FluxMap.java:134)
at reactor.core.publisher.FluxFilter$FilterSubscriber.onError(FluxFilter.java:157)
at reactor.core.publisher.FluxSwitchOnFirst$AbstractSwitchOnFirstMain.onError(FluxSwitchOnFirst.java:572)
at reactor.core.publisher.FluxWindowPredicate$WindowFlux.checkTerminated(FluxWindowPredicate.java:765)
at reactor.core.publisher.FluxWindowPredicate$WindowFlux.drainRegular(FluxWindowPredicate.java:662)
at reactor.core.publisher.FluxWindowPredicate$WindowFlux.drain(FluxWindowPredicate.java:748)
at reactor.core.publisher.FluxWindowPredicate$WindowFlux.onError(FluxWindowPredicate.java:806)
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.signalAsyncError(FluxWindowPredicate.java:352)
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.checkTerminated(FluxWindowPredicate.java:536)
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drainLoop(FluxWindowPredicate.java:502)
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.drain(FluxWindowPredicate.java:432)
at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.onError(FluxWindowPredicate.java:291)
at reactor.core.publisher.FluxCreate$BaseSink.error(FluxCreate.java:474)
at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:802)
at reactor.core.publisher.FluxCreate$BufferAsyncSink.error(FluxCreate.java:747)
at reactor.core.publisher.FluxCreate$SerializedFluxSink.drainLoop(FluxCreate.java:237)
at reactor.core.publisher.FluxCreate$SerializedFluxSink.drain(FluxCreate.java:213)
at reactor.core.publisher.FluxCreate$SerializedFluxSink.error(FluxCreate.java:189)
at org.springframework.http.codec.multipart.MultipartParser.emitError(MultipartParser.java:180)
at org.springframework.http.codec.multipart.MultipartParser$BodyState.onComplete(MultipartParser.java:609)
at org.springframework.http.codec.multipart.MultipartParser.hookOnComplete(MultipartParser.java:125)
at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.netty5.channel.FluxReceive.terminateReceiver(FluxReceive.java:446)
at reactor.netty5.channel.FluxReceive.drainReceiver(FluxReceive.java:258)
at reactor.netty5.channel.FluxReceive.onInboundComplete(FluxReceive.java:382)
at reactor.netty5.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:423)
at reactor.netty5.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:577)
at reactor.netty5.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:110)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at reactor.netty5.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:251)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.ByteToMessageDecoder$ByteToMessageDecoderContext.fireChannelRead(ByteToMessageDecoder.java:446)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder$1.fireChannelRead(HttpServerCodec.java:134)
at io.netty5.handler.codec.http.HttpObjectDecoder.decode(HttpObjectDecoder.java:387)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder.decode(HttpServerCodec.java:116)
at io.netty5.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:387)
at io.netty5.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:330)
at io.netty5.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:204)
at io.netty5.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:230)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.ChannelHandler.channelRead(ChannelHandler.java:235)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:838)
at io.netty5.channel.AbstractChannel$ReadSink.processRead(AbstractChannel.java:1975)
at io.netty5.channel.nio.AbstractNioByteChannel.doReadNow(AbstractNioByteChannel.java:74)
at io.netty5.channel.AbstractChannel$ReadSink.readLoop(AbstractChannel.java:2035)
at io.netty5.channel.AbstractChannel.readNow(AbstractChannel.java:910)
at io.netty5.channel.nio.AbstractNioChannel.access$100(AbstractNioChannel.java:42)
at io.netty5.channel.nio.AbstractNioChannel$1.handle(AbstractNioChannel.java:108)
at io.netty5.channel.nio.NioHandler.processSelectedKey(NioHandler.java:506)
at io.netty5.channel.nio.NioHandler.processSelectedKeysOptimized(NioHandler.java:489)
at io.netty5.channel.nio.NioHandler.processSelectedKeys(NioHandler.java:430)
at io.netty5.channel.nio.NioHandler.run(NioHandler.java:407)
at io.netty5.channel.SingleThreadEventLoop.runIO(SingleThreadEventLoop.java:192)
at io.netty5.channel.SingleThreadEventLoop.run(SingleThreadEventLoop.java:176)
at io.netty5.util.concurrent.SingleThreadEventExecutor.lambda$doStartThread$4(SingleThreadEventExecutor.java:774)
at io.netty5.util.internal.ThreadExecutorMap.lambda$apply$1(ThreadExecutorMap.java:68)
at io.netty5.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.springframework.core.codec.DecodingException: Could not find end of body (␍␊--IPfFMKLDj72s9d-CAWfR9E36mWg2R8ubNDR2S-h)
at org.springframework.http.codec.multipart.MultipartParser$BodyState.onComplete(MultipartParser.java:609)
at org.springframework.http.codec.multipart.MultipartParser.hookOnComplete(MultipartParser.java:125)
at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.netty5.channel.FluxReceive.terminateReceiver(FluxReceive.java:446)
at reactor.netty5.channel.FluxReceive.drainReceiver(FluxReceive.java:258)
at reactor.netty5.channel.FluxReceive.onInboundComplete(FluxReceive.java:382)
at reactor.netty5.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:423)
at reactor.netty5.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:577)
at reactor.netty5.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:110)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at reactor.netty5.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:251)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.ByteToMessageDecoder$ByteToMessageDecoderContext.fireChannelRead(ByteToMessageDecoder.java:446)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder$1.fireChannelRead(HttpServerCodec.java:134)
at io.netty5.handler.codec.http.HttpObjectDecoder.decode(HttpObjectDecoder.java:387)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder.decode(HttpServerCodec.java:116)
at io.netty5.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:387)
at io.netty5.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:330)
at io.netty5.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:204)
at io.netty5.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:230)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.ChannelHandler.channelRead(ChannelHandler.java:235)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:838)
at io.netty5.channel.AbstractChannel$ReadSink.processRead(AbstractChannel.java:1975)
at io.netty5.channel.nio.AbstractNioByteChannel.doReadNow(AbstractNioByteChannel.java:74)
at io.netty5.channel.AbstractChannel$ReadSink.readLoop(AbstractChannel.java:2035)
at io.netty5.channel.AbstractChannel.readNow(AbstractChannel.java:910)
at io.netty5.channel.nio.AbstractNioChannel.access$100(AbstractNioChannel.java:42)
at io.netty5.channel.nio.AbstractNioChannel$1.handle(AbstractNioChannel.java:108)
at io.netty5.channel.nio.NioHandler.processSelectedKey(NioHandler.java:506)
at io.netty5.channel.nio.NioHandler.processSelectedKeysOptimized(NioHandler.java:489)
at io.netty5.channel.nio.NioHandler.processSelectedKeys(NioHandler.java:430)
at io.netty5.channel.nio.NioHandler.run(NioHandler.java:407)
at io.netty5.channel.SingleThreadEventLoop.runIO(SingleThreadEventLoop.java:192)
at io.netty5.channel.SingleThreadEventLoop.run(SingleThreadEventLoop.java:176)
at io.netty5.util.concurrent.SingleThreadEventExecutor.lambda$doStartThread$4(SingleThreadEventExecutor.java:774)
at io.netty5.util.internal.ThreadExecutorMap.lambda$apply$1(ThreadExecutorMap.java:68)
at io.netty5.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
2022-10-23 21:56:28,579 ERROR [reactor-http-nio-2] reactor.util.Loggers$Slf4JLogger: Operator called default onErrorDropped
org.springframework.core.codec.DecodingException: Could not find end of body (␍␊--IPfFMKLDj72s9d-CAWfR9E36mWg2R8ubNDR2S-h)
at org.springframework.http.codec.multipart.MultipartParser$BodyState.onComplete(MultipartParser.java:609)
at org.springframework.http.codec.multipart.MultipartParser.hookOnComplete(MultipartParser.java:125)
at reactor.core.publisher.BaseSubscriber.onComplete(BaseSubscriber.java:197)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
at reactor.netty5.channel.FluxReceive.terminateReceiver(FluxReceive.java:446)
at reactor.netty5.channel.FluxReceive.drainReceiver(FluxReceive.java:258)
at reactor.netty5.channel.FluxReceive.onInboundComplete(FluxReceive.java:382)
at reactor.netty5.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:423)
at reactor.netty5.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:577)
at reactor.netty5.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:110)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at reactor.netty5.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:251)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.ByteToMessageDecoder$ByteToMessageDecoderContext.fireChannelRead(ByteToMessageDecoder.java:446)
at io.netty5.channel.internal.DelegatingChannelHandlerContext.fireChannelRead(DelegatingChannelHandlerContext.java:113)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder$1.fireChannelRead(HttpServerCodec.java:134)
at io.netty5.handler.codec.http.HttpObjectDecoder.decode(HttpObjectDecoder.java:387)
at io.netty5.handler.codec.http.HttpServerCodec$HttpServerRequestDecoder.decode(HttpServerCodec.java:116)
at io.netty5.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:387)
at io.netty5.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:330)
at io.netty5.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:204)
at io.netty5.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:230)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelHandlerContext.findAndInvokeChannelRead(DefaultChannelHandlerContext.java:445)
at io.netty5.channel.DefaultChannelHandlerContext.fireChannelRead(DefaultChannelHandlerContext.java:426)
at io.netty5.channel.ChannelHandler.channelRead(ChannelHandler.java:235)
at io.netty5.channel.DefaultChannelHandlerContext.invokeChannelRead(DefaultChannelHandlerContext.java:455)
at io.netty5.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:838)
at io.netty5.channel.AbstractChannel$ReadSink.processRead(AbstractChannel.java:1975)
at io.netty5.channel.nio.AbstractNioByteChannel.doReadNow(AbstractNioByteChannel.java:74)
at io.netty5.channel.AbstractChannel$ReadSink.readLoop(AbstractChannel.java:2035)
at io.netty5.channel.AbstractChannel.readNow(AbstractChannel.java:910)
at io.netty5.channel.nio.AbstractNioChannel.access$100(AbstractNioChannel.java:42)
at io.netty5.channel.nio.AbstractNioChannel$1.handle(AbstractNioChannel.java:108)
at io.netty5.channel.nio.NioHandler.processSelectedKey(NioHandler.java:506)
at io.netty5.channel.nio.NioHandler.processSelectedKeysOptimized(NioHandler.java:489)
at io.netty5.channel.nio.NioHandler.processSelectedKeys(NioHandler.java:430)
at io.netty5.channel.nio.NioHandler.run(NioHandler.java:407)
at io.netty5.channel.SingleThreadEventLoop.runIO(SingleThreadEventLoop.java:192)
at io.netty5.channel.SingleThreadEventLoop.run(SingleThreadEventLoop.java:176)
at io.netty5.util.concurrent.SingleThreadEventExecutor.lambda$doStartThread$4(SingleThreadEventExecutor.java:774)
at io.netty5.util.internal.ThreadExecutorMap.lambda$apply$1(ThreadExecutorMap.java:68)
at io.netty5.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:833)
But the unit test using WebTestClient is passed.

Get the support from Spring developers, there are several problems caused the issue.
There is a bug in the new JdkClientConnector, fixed in Spring 6.0.0-RC3.
We have to consume the FilePartEvent, event in this demo, we have no further step to process it.
In the formEvent and fileEvent handling, we have to append an extra \n to text, thus Mono.just() can be concat into a Flux correctly.

Related

HTTP/2 client preface string missing or corrupt for C client gRPC using HTTPClient

I am getting "HTTP/2 client preface string missing or corrupt."
My thoughts are that it has to do with the headers not being set correctly. It is likely the implementation of WifiClient/WifiSecureClient. I've been thinking about this for over several weeks and I'm stuck. Any advice?
[Updated: Answer below]
The client was generated using the nanopb protocol buffer compiler:
protoc --plugin=protoc-gen-nanopb=~/grpc/nanopb/generator/protoc-gen-nanopb --nanopb_out=. helloworld.proto
Arduino client:
DHT dht(DHTPIN, DHTTYPE);
WiFiClient client;
//WiFiClientSecure client;
void setup() {
Serial.setDebugOutput(true);
Serial.begin(115200);
delay(10);
WiFi.begin("<SSID>", "<My Password>");
delay(3000);
while (WiFi.status() != WL_CONNECTED) {
Serial.println("WIFI connection failed, reconnecting...");
delay(2000);
}
Serial.print("WiFi connected, ");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Starting DHT11 sensor...");
dht.begin();
}
void loop() {
Serial.print("connecting to ");
Serial.println(addr);
// client.setInsecure();
if (!client.connect(addr, port)) {
Serial.println(addr);
Serial.println(port);
Serial.println("connection failed");
Serial.println("wait 5 sec to reconnect...");
delay(5000);
return;
}
Serial.println("reading humidity/temp...");
float hum = dht.readHumidity();
float tmp = dht.readTemperature(true);
Serial.println(hum);
Serial.println(tmp);
if (isnan(hum) || isnan(tmp)) {
Serial.println("failed to read sensor data");
delay(2000);
return;
}
float hiCel = dht.computeHeatIndex(tmp, hum, true);
helloworld_TempEvent temp = helloworld_TempEvent_init_zero;
temp.deviceId = 1;
temp.eventId = 0;
temp.humidity = hum;
temp.tempCel = tmp;
temp.heatIdxCel = hiCel;
sendTemp(temp);
delay(1000);
}
void sendTemp(helloworld_TempEvent e) {
uint8_t buffer[128];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&stream, helloworld_TempEvent_fields, &e)) {
Serial.println("failed to encode temp proto");
Serial.println(PB_GET_ERROR(&stream));
return;
}
Serial.print("sending temp... ");
Serial.println(e.tempCel);
client.write(buffer, stream.bytes_written);
}
The server was generated using the standard java protocol buffer compiler. The only thing I changed was adding a TempEvent (below).
... (helloworld template stuff) ...
// The request message containing temperatures
message TempEvent {
int32 deviceId = 1;
int32 eventId = 2;
float humidity = 3;
float tempCel = 4;
float heatIdxCel = 5;
}
The sample java client works without any issues. Where my problem lies is the simple client using nanopb on an ESP8266-01 wifi module which is sending the data using gRPC.
public class Server {
// Doesn't work
public static void main(String[] args) throws IOException, InterruptedException {
io.grpc.Server server = ServerBuilder
.forPort(8080)
.addService(new HelloServiceImpl()).build();
server.start();
server.awaitTermination();
}
// Works just fine
public static void main(String[] args) throws IOException, InterruptedException {
try (ServerSocket server = new ServerSocket(8080)) {
System.out.println("Server accepting connections on port " + server.getLocalPort());
TemperatureClient tempClient = new TemperatureClient();
while(true) {
Socket client = server.accept();
System.out.println("Client connected using remote port " + client.getPort());
final Thread t = new Thread(() -> {
try {
TempEvent p = TempEvent.parseFrom(client.getInputStream());
float i = p.getTempCel();
System.out.println("TEMP " + i);
} catch (IOException ioe) {
ioe.printStackTrace();
}
});
t.start();
}
}
}
The client is able to hit the server:
Nov 29, 2021 5:49:30 PM io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
INFO: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 080c10641d0000d84125e17aa0422de4459e42
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:108)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:306)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:239)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:831)
To debug this, I wanted to first see if I could use grpcurl, but I get this:
localhost#pro ~ % grpcurl -plaintext localhost:50051 list
Failed to list services: server does not support the reflection API
localhost#pro ~ % grpcurl -insecure localhost:50051 list
Failed to dial target host "localhost:50051": tls: first record does not look like a TLS handshake
I started looking into the implementation for WifiClient.h used in my implementation code, but does anyone have any ideas on a simple way to test this without digging into everything? I was thinking this should be super simple... but it is turning out to be much more entailed to generate a simple client than I thought. I feel like I am missing something here.
From other forums on here: "The client and server aren't agreeing. Typically this is because one is plaintext and the other using TLS. But it can also be due to HTTP/1 vs HTTP/2 in certain environments."
After looking at the Go Lang implementation, I just tried using WiFiClientSecure client.setInsecure(); // didn't work and the hex dump is below.
17:36:33.030 [grpc-nio-worker-ELG-3-1] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler - [id: 0x41b96938, L:/192.168.0.23:8080 - R:/192.168.0.24:61587] OUTBOUND SETTINGS: ack=false settings={MAX_CONCURRENT_STREAMS=2147483647, INITIAL_WINDOW_SIZE=1048576, MAX_HEADER_LIST_SIZE=8192}
17:36:33.031 [grpc-nio-worker-ELG-3-1] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler - [id: 0x41b96938, L:/192.168.0.23:8080 - R:/192.168.0.24:61587] OUTBOUND WINDOW_UPDATE: streamId=0 windowSizeIncrement=983041
17:36:33.063 [grpc-nio-worker-ELG-3-1] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler - [id: 0x41b96938, L:/192.168.0.23:8080 - R:/192.168.0.24:61587] OUTBOUND GO_AWAY: lastStreamId=2147483647 errorCode=1 length=126 bytes=485454502f3220636c69656e74207072656661636520737472696e67206d697373696e67206f7220636f72727570742e204865782064756d7020666f72207265...
17:36:33.064 [grpc-nio-worker-ELG-3-1] DEBUG io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler - [id: 0x41b96938, L:/192.168.0.23:8080 - R:/192.168.0.24:61587] Sent GOAWAY: lastStreamId '2147483647', errorCode '1', debugData 'HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030100d4010000d00303000000005c2f03aae7147c5f36'. Forcing shutdown of the connection.
Dec 10, 2021 5:36:33 PM io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
INFO: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030100d4010000d00303000000005c2f03aae7147c5f36
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:108)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:306)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:239)
at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:438)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)
at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
at io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
at io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.grpc.netty.shaded.io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
at io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
at io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.grpc.netty.shaded.io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.grpc.netty.shaded.io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:831)
WiFiClient client;
if (!client.connect(addr, port)) {
This forms a basic TCP connection. However, gRPC is a complex protocol based on HTTP/2. Currently you are just writing raw protobuf messages to a TCP socket, which can work for communication but is certainly not what a gRPC server is expecting.
Nanopb does not have gRPC support in itself. There is a third-party project adding it, but it is currently unmaintained.

Connection closed while handshake in process with websockets

I have a problem when sending a message to a websocket server.
2020-06-15 17:06:51,830 INFO [snc.mob.gro.myt.con.ws.MutinyWebsocket] (vert.x-eventloop-thread-6) erreur {}: io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: Connection closed while handsh
ake in process
at io.vertx.core.http.impl.WebSocketHandshakeInboundHandler.channelInactive(WebSocketHandshakeInboundHandler.java:57)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:260)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:246)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:239)
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelInactive(CombinedChannelDuplexHandler.java:418)
at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:386)
at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:351)
at io.netty.handler.codec.http.HttpClientCodec$Decoder.channelInactive(HttpClientCodec.java:288)
at io.netty.channel.CombinedChannelDuplexHandler.channelInactive(CombinedChannelDuplexHandler.java:221)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:260)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:246)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelInactive(AbstractChannelHandlerContext.java:239)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelInactive(DefaultChannelPipeline.java:1405)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:260)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:246)
at io.netty.channel.DefaultChannelPipeline.fireChannelInactive(DefaultChannelPipeline.java:901)
at io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:818)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:164)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:472)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:497)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
I tried with some chrome plugins and everything works fine.
Here is my code :
public void send(final String message){
WebSocketConnectOptions wsoptions = new WebSocketConnectOptions()
.setSsl(Boolean.FALSE).setURI( "/" ).setPort( 9304 ).setHost( "myserver.com" );
HttpClientOptions httpClientOptions = new HttpClientOptions( )
.setTrustAll( true ).setSsl( false );
Uni< WebSocket > webSocketUni = vertx.createHttpClient(httpClientOptions).webSocket( wsoptions );
webSocketUni
.subscribe()
.with( a -> {
a.handler( data -> {
log.info( "server message {}", data.toString() );
a.writeTextMessageAndForget( message );
} );
},
e -> log.info( "erreur {}", e ))
;
}
I don't know what's the handshake problem with my client.
Any help really appreciated.
Thank you!

Why is mstor throwing a NotSerializableException on inbox.close()

I'm using the basic mstor logic with version 1.0.0 of of the maven mstor library, but it's throwing an Exception on the inbox.close() method. Note: I am not doing any writing to the disk so this Exception is odd. I made, as an attempt, my class which calls this code to "implement Serializable", but that did not help.
This code is running from a SpringBoot REST Service.
If I don't do an inbox.close(), then on Windows the mbox file is still open (not released by this library) after the method below completes.
Here's the basic code:
Properties properties = new Properties();
properties.setProperty("mail.store.protocol", "mstor");
properties.setProperty("mstor.mbox.metadataStrategy", "none");
properties.setProperty("mstor.mbox.cacheBuffers", "disabled");
properties.setProperty("mstor.mbox.bufferStrategy", "mapped");
properties.setProperty("mstor.metadata", "disabled");
properties.setProperty("mstor.mozillaCompatibility", "enabled");
Session session = Session.getInstance(properties);
try {
store = session.getStore(new URLName("mstor:" + pathToMboxFile));
store.connect();
inbox = (MStorFolder) store.getDefaultFolder();
inbox.open(Folder.READ_ONLY);
Message[] messages = inbox.getMessages();
int bodyPartCount = 0;
// ***********************
// process all mbox data.
// *************************
for (int pos = 0; pos < messages.length; pos++)
{
// processing.
}
catch (NoSuchProviderException e)
{
log.debug("MboxController NoSuchProviderException Exception: " + e.getMessage());
}
catch (javax.mail.MessagingException e)
{
errors.append(e);
log.debug("MboxController MessagingException Exception: " + e.getMessage());
}
finally
{
// close the mbox store
try
{
inbox.close(false);
store.close();
}
catch (MessagingException e)
{
log.debug("MboxController Closing Store Exception: " + e.getMessage());
}
}
Now, although the code does work and returns the mbox text and closes the file, when the inbox.close(false) runs, I get this error stack (or something close to it) each time in the Tomcat log:
2019-03-31 08:06:09 - Disk Write of 191 failed:
"java.io.NotSerializableException: net.fortuna.mstor.data.MessageInputStream
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.defaultWriteObject(ObjectOutputStream.java:441)
at net.sf.ehcache.Element.writeObject(Element.java:791)
at sun.reflect.GeneratedMethodAccessor88.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1140)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at net.sf.ehcache.util.MemoryEfficientByteArrayOutputStream.serialize(MemoryEfficientByteArrayOutputStream.java:97)
at net.sf.ehcache.store.disk.DiskStorageFactory.serializeElement(DiskStorageFactory.java:413)
at net.sf.ehcache.store.disk.DiskStorageFactory.write(DiskStorageFactory.java:392)
at net.sf.ehcache.store.disk.DiskStorageFactory$DiskWriteTask.call(DiskStorageFactory.java:493)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1151)
at net.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1135)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)

Unicode receiving with ASP.NET core websocket

I'm trying to build a websocket server using ASP.NET core 1.1 websocket middleware that can handle text messages. My strategy is to use a fixed-size buffer to keep reading and decoding until the websocket message ends.
The middleware setup goes like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
var logger = loggerFactory.CreateLogger("websocket");
app.UseWebSockets();
app.Use(async (http, next) =>
{
if (!http.WebSockets.IsWebSocketRequest)
{
await next();
return;
}
var websocket = await http.WebSockets.AcceptWebSocketAsync();
while (websocket.State == WebSocketState.Open)
{
var buffer = new ArraySegment<byte>(new byte[32]);
var charbuffer = new char[32];
try
{
var sb = new StringBuilder();
var decoder = Encoding.UTF8.GetDecoder();
//aha, we got a message
var detectResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
var receiveResult = detectResult;
while (!receiveResult.EndOfMessage)
{
var charLen = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
logger.LogInformation($"Decoded {charLen} byte(s) from wire");
sb.Append(charbuffer, 0, charLen);
receiveResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
}
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
logger.LogInformation($"Decoded {charLenFinal} byte(s) from wire");
sb.Append(charbuffer, 0, charLenFinal);
var message = sb.ToString();
logger.LogInformation($"decoded message: {message}");
await websocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes("got it")), WebSocketMessageType.Text, true, CancellationToken.None);
}
catch (Exception ex)
{
logger.LogError(ex.Message);
logger.LogError(ex.InnerException?.Message ?? string.Empty);
}
}
});
}
Now the code works well for text that include only ASCII characters. But when I tried to send Unicode text message (Vietnamese) whose length is longer than the buffer size, an exception occurs
fail: websocket[0]
The remote party closed the WebSocket connection without completing the close handshake.
fail: websocket[0]
at System.Net.WebSockets.ManagedWebSocket.<ReceiveAsyncPrivate>d__60.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at MvcApp.Startup.<>c__DisplayClass5_0.<<Configure>b__0>d.MoveNext()
The exception occurs at the line
var detectResult = await websocket.ReceiveAsync(buffer, CancellationToken.None);
What could be the reason for this error?
I think this:
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0);
should be:
var charLenFinal = decoder.GetChars(buffer.Array, 0, receiveResult.Count, charbuffer, 0, true);
since:
https://msdn.microsoft.com/en-us/library/125z2etb(v=vs.110).aspx
Remember that the Decoder object saves state between calls to
GetChars. When the application is done with a stream of data, it
should set the flush parameter to true to make sure that the state
information is flushed. With this setting, the decoder ignores invalid
bytes at the end of the data block and clears the internal buffer
.

Plotting Image Histogram Using Androidplot

I m a beginner programmer, I am developing an android application which process an image and generate its histogram. I am using AndroidPlot to draw different color channels of an image in the histogram, but I'm having some errors and exceptions.
This is my code :
public class ImgHistogram extends Activity {
ImageView ImageView;
LinearLayout ll;
ImageView imgView;
private Bitmap source ;
private XYPlot xyPlot;
public XYSeries rSerie;
int[] intensities={
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,
101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,
201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
} ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.histo);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
imgView = (ImageView) findViewById(R.id.img);
Bundle bundle = this.getIntent().getExtras();
source=(Bitmap) bundle.getParcelable("img");
imgView.setImageBitmap(source);
// initialize our XYPlot reference:
xyPlot = (XYPlot) findViewById(R.id.xyplot);
Bitmap image = Bitmap.createBitmap(source.getWidth(),source.getHeight(), Bitmap.Config.ARGB_8888);
ArrayList<Integer> rVals = new ArrayList<Integer>();
for (int x = 0; x < image.getWidth(); x++) {
for (int y = 0; y < image.getHeight(); y++) {
int color = image.getPixel(x, y);
rVals.add((color >> 16) & 0xff); //red channel
}}
rSerie = new SimpleXYSeries(
rVals, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY, "red channel");
// Create a formatter to format Line and Point of income series
LineAndPointFormatter format = new LineAndPointFormatter(
Color.rgb(0, 0, 255), // line color
Color.rgb(200, 200, 200), // point color
Color.rgb(10, 20, 20), // fill color (none)
null );
// add series to the xyplot:
xyPlot.addSeries(rSerie, format);
// Formatting the Domain Values ( X-Axis )
xyPlot.setDomainValueFormat(new Format() {
private static final long serialVersionUID = 1L;
#Override
public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) {
return new StringBuffer( intensities[ ( (Number)obj).intValue() ] );
}
#Override
public Object parseObject(String source, ParsePosition pos) {
return null;
}
});
xyPlot.setDomainLabel("");
xyPlot.setRangeLabel("");
// Increment X-Axis by 1 value
xyPlot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1);
xyPlot.getGraphWidget().setRangeLabelWidth(50);
// Reduce the number of range labels
xyPlot.setTicksPerRangeLabel(2);
// Reduce the number of domain labels
xyPlot.setTicksPerDomainLabel(2);
// Remove all the developer guides from the chart
// xyPlot.disableAllMarkup();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
and this is the stack trace:
05-23 23:52:21.731: E/AndroidRuntime(30404): FATAL EXCEPTION: main
05-23 23:52:21.731: E/AndroidRuntime(30404): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.dreamaker.magipixel/com.dreamaker.magipixel.processing.ImgHistogram}: java.lang.NullPointerException
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2295)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2349)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread.access$700(ActivityThread.java:159)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1316)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.os.Handler.dispatchMessage(Handler.java:99)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.os.Looper.loop(Looper.java:137)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread.main(ActivityThread.java:5419)
05-23 23:52:21.731: E/AndroidRuntime(30404): at java.lang.reflect.Method.invokeNative(Native Method)
05-23 23:52:21.731: E/AndroidRuntime(30404): at java.lang.reflect.Method.invoke(Method.java:525)
05-23 23:52:21.731: E/AndroidRuntime(30404): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
05-23 23:52:21.731: E/AndroidRuntime(30404): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
05-23 23:52:21.731: E/AndroidRuntime(30404): at dalvik.system.NativeStart.main(Native Method)
05-23 23:52:21.731: E/AndroidRuntime(30404): Caused by: java.lang.NullPointerException
05-23 23:52:21.731: E/AndroidRuntime(30404): at com.dreamaker.magipixel.processing.ImgHistogram.onCreate(ImgHistogram.java:110)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.Activity.performCreate(Activity.java:5372)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1104)
05-23 23:52:21.731: E/AndroidRuntime(30404): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2257)
05-23 23:52:21.731: E/AndroidRuntime(30404): ... 11 more
I hope that someone could help me!
Given the fact that in the line in question (ImgHistogram.java:110) you have only 1 object which could possibly cause the exception - xyPlot:
xyPlot.addSeries(rSerie, format);
This suggests that findViewByID returns null =< xyPlot is null. There is a question about this: findViewByID returns null the accepted answer should solve your problem:
Wait until onFinishInflate()

Resources