rSocket websocket postman testing mime types and endpoints - websocket

I am using spring-boot-starter-webflux and spring-boot-starter-rsocket version 2.7.1
The rSocket transport is set to websocket like this:
spring.rsocket.server.transport=websocket
spring.rsocket.server.mapping-path=/rsocket
# this setting has no effect when transport==WEBSOCKET
spring.rsocket.server.port=7000
There's a spring #Controller endpoint #MessageMapping setup for a simple string like:
#MessageMapping("test")
String test() {
Logs.Info("*** Received test ***");
return "tested";
}
I want to get a successful test done with Postman. Run the spring boot app locally and connect to ws://localhost:7000 using mime types
dataMimeType: 'application/json'
metadataMimeType: 'message/x.rsocket.routing.v0'
Like this:
The rsocket websocket connects, but I can't hit the endpoint test
With error 1005 No Status Received: Missing status code even though one was expected
On the server the error is
DEBUG [reactor-http-nio-2] debug: [c4e97d34-1, L:/127.0.0.1:7000 - R:/127.0.0.1:2051] Cancelling Websocket inbound. Closing Websocket
DEBUG [reactor-http-nio-2] debug: [c4e97d34, L:/127.0.0.1:7000 - R:/127.0.0.1:2051] Removed handler: PongHandler, pipeline: DefaultChannelPipeline{(wsencoder = io.netty.handler.codec.http.websocketx.WebSocket13FrameEncoder), (wsdecoder = io.netty.handler.codec.http.websocketx.WebSocket13FrameDecoder), (reactor.right.reactiveBridge = reactor.netty.channel.ChannelOperationsHandler)}
DEBUG [reactor-http-nio-2] debug: [c4e97d34, L:/127.0.0.1:7000 ! R:/127.0.0.1:2051] An outbound error could not be processed
java.nio.channels.ClosedChannelException
at reactor.core.publisher.MonoErrorSupplied.call(MonoErrorSupplied.java:61)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:228)
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
at reactor.core.publisher.SinkEmptyMulticast$VoidInner.complete(SinkEmptyMulticast.java:238)
at reactor.core.publisher.SinkEmptyMulticast.tryEmitEmpty(SinkEmptyMulticast.java:70)
at reactor.core.publisher.SinkEmptySerialized.tryEmitEmpty(SinkEmptySerialized.java:46)
What's the incorrect setting in Postman?

The answer is don't use postman. Rsocket is a binary protocol, Even though based on Websocket, There are many tools test it.
use spring message write a unit test
#Autowired
private RSocketRequester rSocketRequester;
StepVerifier.create(rSocketRequester
.route("test")
.retrieveMono(String.class))
.expectNext("tested")
.verifyComplete();
RSocket Client CLI (RSC)
rsc --request --route=test --debug ws://localhost:7000/rsocket

Actually the following message was received:
{
"data":"test",
"metadata":4
}
Per screenshot
But now the error on the server side is:
DEBUG [reactor-http-nio-6] lambda$receive$0: receiving ->
Frame => Stream ID: 2064452128 Type: REQUEST_N Flags: 0b100000 Length: 42
RequestN: 539124833
Data:
DEBUG [reactor-http-nio-6] sendErrorAndClose: sending -> InvalidSetupException: SETUP or RESUME frame must be received before any others

Related

Break in fabric8 kubernetes client mock server

We were using fabric8 kubernetes client 5.3.x for watcher and it worked fine. Recently when we moved to 5.11.2 there were many changes observed and eventually the JUnit Tests started failing.
We use io.fabric8.kubernetes.client.server.mock.KubernetesServer
Earlier we were using ContainerStatus .withNewReady which now seem to be removed.
And then we added the following annotation
#Rule
public KubernetesServer myMockServer = new KubernetesServer(false, true);
After this, we are getting the following logs stating unsupported label requirement while the application code is sending this label.
[2022-02-03T07:30:22.733Z] 07:30:17.812 [pool-1-thread-1] DEBUG io.fabric8.kubernetes.client.dsl.internal.AbstractWatchManager - Watching http://localhost:40033/api/v1/namespaces/test/pods?labelSelector=app.kubernetes.io%2Fname%20in%20%28apps%29&timeoutSeconds=0&allowWatchBookmarks=true&watch=true...
[2022-02-03T07:30:22.733Z] 07:30:17.814 [MockWebServer /127.0.0.1:49882] WARN io.fabric8.kubernetes.client.server.mock.KubernetesAttributesExtractor - Ignoring unsupported label requirement: app.kubernetes.io/name in (apps)
[2022-02-03T07:30:22.733Z] 07:30:17.815 [MockWebServer /127.0.0.1:49882] DEBUG io.fabric8.kubernetes.client.server.mock.KubernetesAttributesExtractor - fromPath /api/v1/namespaces/test/pods?labelSelector=app.kubernetes.io%2Fname%20in%20%28apps%29&timeoutSeconds=0&allowWatchBookmarks=true&watch=true : {attributes: {namespace={key:namespace, value:test}, version={key:version, value:v1}, plural={key:plural, value:pods}}}
[2022-02-03T07:30:22.733Z] 07:30:17.815 [OkHttp http://localhost:40033/...] DEBUG io.fabric8.kubernetes.client.dsl.internal.WatcherWebSocketListener - WebSocket successfully opened
[2022-02-03T07:30:22.733Z] 07:30:20.818 [OkHttp http://localhost:40033/...] DEBUG io.fabric8.kubernetes.client.dsl.internal.AbstractWatchManager - Scheduling reconnect task
Is there something more should be done? Tests are in ERROR state and not Failed.
Sample JUnit program
public void testAddNewPodWatchEvent()
{
//given
doReturn(myClientMock).when(myTestObj).getClient();
doReturn(myWatcherSpy).when(myTestObj).createEventHandler();
String PATH =
"/api/v1/namespaces/test/pods?labelSelector=app.kubernetes.io%2Fname%20in%20%28apps%29&timeoutSeconds=0&watch=true";
Map<String, String> mockLabelMap = new HashMap<>();
mockLabelMap.put("foo", "testlabel");
mockLabelMap.put("app.kubernetes.io/name", "apps");
Pod accPod = createAppsPod(mockLabelMap, true);
myMockServer.expect()
.get()
.withPath(PATH)
.andUpgradeToWebSocket()
.open()
.waitFor(100)
.andEmit(new WatchEvent(accPod, "ADDED"))
.done()
.once();
//when
myTestObj.activate(mockProps);
sleepForWatchToBeInvoked();
//then
verify(myWatcherSpy, atLeastOnce()).eventReceived(Action.ADDED, accPod);
}

STOMP over WebSockets: Spring Boot expects JSON; NodeJs STOMP.js client fails to connect

When trying out STOMP over WebSockets, I noticed inconsistencies between different implementations, namely between a Spring Boot Java implementation and a NodeJs client written with STOMP.js.
When debugging into it, the difference is that in the Spring Boot app, the CONNECT message is expected to be a JSON array. For instance, this message is sent by their test client (written in JavaScript using the SocksJS library):
["CONNECT\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000"]
In contrast, my NodeJs STOMP.js test client (code is below) sends the following frame:
CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
^#
Unfortunately, I am not experienced with STOMP, but after reading through the specification, I did not understand why Spring Boot expects the data to be represented as a JSON array. Is this a known problem?
To demonstrate, let me share two example runs. One successful run to connect to RabbitMQ, followed by a failed attempt to connect against the Java Spring Boot app. (A reproducible setup with the code can be found at the end.)
Connect to RabbitMQ instance, which is configure to use STOMP over WebSockets (running on ws://localhost:15674/ws):
$ node client.js
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< CONNECTED
server:RabbitMQ/3.8.8
session:session-WkKD6rN5BNc_ObKpziikYA
heart-beat:4000,4000
version:1.2
connected to server RabbitMQ/3.8.8
send PING every 4000ms
check PONG every 4000ms
onConnect called
<<< PONG
Received data
<<<
<<< PONG
>>> PING
Received data
<<<
Now connect (unsuccessfully) against the Spring Boot app (ws://localhost:5555/chat/123/k2qn3dl7/websocket):
node client.js
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< o
Received data
<<< c[1007,""]
Connection closed to ws://localhost:5555/chat/123/k2qn3dl7/websocket
STOMP: scheduling reconnection in 5000ms
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
Received data
<<< o
^C
The reason why it fails is that Jackson (the JSON parser) failed to parse that payload:
CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
^#
As said, in the client that comes with the Spring Boot example, the payload looked like that:
["CONNECT\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000"]
Here is the full error in the Spring Boot app:
2021-07-22 13:58:59.546 INFO 74313 --- [nio-5555-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2021-07-22 13:58:59.594 ERROR 74313 --- [nio-5555-exec-1] s.w.s.s.t.s.WebSocketServerSockJsSession : Broken data received. Terminating WebSocket connection abruptly
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'CONNECT': was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
at [Source: (String)"CONNECT
accept-version:1.0,1.1,1.2
heart-beat:4000,4000
"; line: 1, column: 8]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2337) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:720) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.java:2903) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:1949) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:781) ~[jackson-core-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4684) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4586) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3548) ~[jackson-databind-2.12.3.jar:2.12.3]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3516) ~[jackson-databind-2.12.3.jar:2.12.3]
at org.springframework.web.socket.sockjs.frame.Jackson2SockJsMessageCodec.decode(Jackson2SockJsMessageCodec.java:64) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.sockjs.transport.session.WebSocketServerSockJsSession.handleMessage(WebSocketServerSockJsSession.java:187) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.sockjs.transport.handler.SockJsWebSocketHandler.handleTextMessage(SockJsWebSocketHandler.java:93) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.handler.AbstractWebSocketHandler.handleMessage(AbstractWebSocketHandler.java:43) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:114) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:43) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:85) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:82) ~[spring-websocket-5.3.8.jar:5.3.8]
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:156) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
at java.base/java.lang.Thread.run(Thread.java:829) ~[na:na]
2021-07-22 13:59:04.610 ERROR 74313 --- [nio-5555-exec-2] s.w.s.s.t.s.WebSocketServerSockJsSession : Broken data received. Terminating WebSocket connection abruptly
Path to reproduce:
NodeJs client code
Spring Boot test app
RabbitMQ test instance
Client code written in NodeJs:
// Required dependencies:
// "#stomp/stompjs": "6.1.0"
// "websocket": "1.0.34"
// Polyfills. For details see:
// https://stomp-js.github.io/guide/stompjs/rx-stomp/ng2-stompjs/pollyfils-for-stompjs-v5.html
Object.assign(global, { WebSocket: require('websocket').w3cwebsocket });
const StompJs = require('#stomp/stompjs');
const client = new StompJs.Client({
//brokerURL: 'ws://localhost:15674/ws', // RabbitMQ (should work)
brokerURL: 'ws://localhost:5555/chat/123/k2qn3dl7/websocket', // Spring app (should fail)
reconnectDelay: 5000,
heartbeatIncoming: 4000,
heartbeatOutgoing: 4000,
logRawCommunication: true,
debug: (x) => console.log(x),
});
client.onConnect = function (frame) {
console.log('onConnect called');
};
client.activate();
The Spring Boot app can be found here. I started it on port 5555:
git clone git#github.com:eugenp/tutorials.git
cd tutorials/spring-websockets
SERVER_PORT=5555 mvn spring-boot:run
Note: if you then go to http://localhost:5555, you will see a chat application served by the Spring Boot app. When you click connect, a STOMP connection will be established.
To start RabbitMQ, you can use the Docker container used for the tests in STOMP.js:
git clone git#github.com:stomp-js/stompjs.git
cd stompjs
sudo docker build -t myrabbitmq rabbitmq/
sudo docker run --rm -p 15674:15674 myrabbitmq
In short: The JSON messages were not "STOMP over native WebSockets" but "STOMP over SocksJS". The additional JSON layer was introduced by the SocksJS protocol, which is used in the Spring Boot example application.
Here is the longer story. It turned out, that my endpoint was wrong. Instead of
'ws://localhost:5555/chat/123/k2qn3dl7/websocket'
it should have been
'ws://localhost:5555/chat'
It had the wrong URI because I was copying the output that I saw in the browser. Instead I should have looked at the configuration:
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
registry.addEndpoint("/chat").withSockJS();
registry.addEndpoint("/chatwithbots");
registry.addEndpoint("/chatwithbots").withSockJS();
}
Now the confusing part. As can be seen from the configuration, the Spring Boot application defines fallbacks with SocksJS.
If you remove the fallback, the confusing error message goes away. Yet when the fallback is active, Spring will try to process the request as SocksJS. That is why it tries to parse the STOMP frame as JSON, which results in the misleading error message.
In addition, I got confused by the JavaScript client used in the Spring Boot example:
function connect() {
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
It is not connected over native WebSocket but over SocksJS. That explains why Firefox shows JSON requests, not the expected STOMP frames.

okhttp3.OkHttpClient - A connection to any_host was leaked.Did you forget to close a response body?

In my Kotlin's project I use okhttp3
Here example to connect to remote host:
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
var ĐžkHttpClient = OkHttpClient() ĐžkHttpClient.newCall(requestNegotiate).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
...
}}
and is's work fine.
But after few minutes I get many times the next message:
2019-02-13 18:39:39,197 675399 [OkHttp ConnectionPool] WARN okhttp3.OkHttpClient - A connection to any_host_com was leaked. Did you forget to close a response body? To see where this was allocated, set the OkHttpClient logger level to FINE: Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);

Spring WebFlux not streaming response

I was expecting this code to stream events to the client (code is in Kotlin but Java is very similar)
#RestController
object CustomerController {
#GetMapping("/load", produces = arrayOf("application/stream+json"))
fun load(): Flux<String> {
var flux = Flux.fromIterable(ResultIterable())
flux.subscribe({println(it)})
return flux
}
}
ResultIterable is an iterable that generates a string on regular intervals. An infinite stream basically.
I don't see any output, it hangs forever.
I do see the string being printed on regular intervals (println(it)).
I am using the following curl:
curl -X GET http://localhost:8080/load -H 'accept: application/stream+json' -H 'cache-control: no-cache' -H 'content-type: application/stream+json'
Your error is here:
flux.subscribe({println(it)})
You subscribe to the Flux and consume it directly in the method.
When this Flux reaches the Reactor Netty HTTP container, there is nothing to consume already.
If you really would like println() each item, consider to use doOnNext() instead and really leave that subscribe() to the container.
Also you have to really follow Server Side Events rules:
The server-side event stream syntax is simple. Set the "Content-Type" header to "text/event-stream".
https://www.w3schools.com/html/html5_serversentevents.asp
So, when I do this:
#GetMapping("/load", produces = [MediaType.TEXT_EVENT_STREAM_VALUE])
fun load() =
Flux.just("foo", "bar", "baz")
.doOnNext({ println(it) })
I start to get Server Side Events in my connected client:
C:\tmp\so50823339>curl -X GET http://localhost:8080/load
data:foo
data:bar
data:baz
C:\tmp\so50823339>
where at the same time I get logs on the server for the mentioned doOnNext():
2018-06-12 17:33:37.453 INFO 6800 --- [ main] c.e.s.s.So50823339ApplicationKt : Started So50823339ApplicationKt in 3.112 seconds (JVM running for 3.924)
foo
bar
baz

Does Spring Cloud Stream Kafka supports embedded headers?

According to this topic:
Kafka Spring Integration: Headers not coming for kafka consumer -
this is no headers support for Kafka
But documentation says:
spring.cloud.stream.kafka.binder.headers
The list of custom headers that will be transported by the binder.
Default: empty.
I can't get it working with spring-cloud-stream-binder-kafka: 1.2.0.RELEASE
SENDING LOG:
MESSAGE (e23885fd-ffd9-42dc-ebe3-5a78467fee1f) SENT :
GenericMessage [payload=...,
headers={
content-type=application/json,
correlationId=51dd90b1-76e6-4b8d-b667-da25f214f383,
id=e23885fd-ffd9-42dc-ebe3-5a78467fee1f,
contentType=application/json,
timestamp=1497535771673
}]
RECEIVING LOG:
MESSAGE (448175f5-2b21-9a44-26b9-85f093b33f6b) RECEIVED BY HANDLER 1:
GenericMessage [payload=...,
headers={
kafka_offset=36,
id=448175f5-2b21-9a44-26b9-85f093b33f6b,
kafka_receivedPartitionId=0,
contentType=application/json;charset=UTF-8,
kafka_receivedTopic=new_patient, timestamp=1497535771715
}]
MESSAGE (448175f5-2b21-9a44-26b9-85f093b33f6b) RECEIVED BY HANDLER 2 :
GenericMessage [payload=...,
headers={
kafka_offset=36,
id=448175f5-2b21-9a44-26b9-85f093b33f6b,
kafka_receivedPartitionId=0,
contentType=application/json;charset=UTF-8,
kafka_receivedTopic=new_patient, timestamp=1497535771715
}]
I expect to see the same message id and get correlationId on receiving side.
application.properties:
spring.cloud.stream.kafka.binder.headers=correlationId
spring.cloud.stream.bindings.newTest.destination=new_test
spring.cloud.stream.bindings.newTestCreated.destination=new_test
spring.cloud.stream.default.consumer.headerMode=embeddedHeaders
spring.cloud.stream.default.producer.headerMode=embeddedHeaders
SENDING MESSAGE:
#Publisher(channel = "testChannel")
public Object newTest(Object param) {
...
return myObject;
}
Yes, it does: http://docs.spring.io/spring-cloud-stream/docs/Chelsea.SR2/reference/htmlsingle/index.html#_consumer_properties
headerMode
When set to raw, disables header parsing on input. Effective only for messaging middleware that does not support message headers natively and requires header embedding. Useful when inbound data is coming from outside Spring Cloud Stream applications.
Default: embeddedHeaders
But that is already Spring Cloud Stream story, not Spring Kafka per se.

Resources