Can Protobuf serialized data be sent from gRPC server to client? - protocol-buffers

In my Grpc server application, I already have the Protobuf serialized data that it received from a different server. Is there a way to avoid deserialization in the gRPC server and avoid creating the Protobuf response object and directly send theProtobuf serialized data to the client so client can do the deserialization ?
my grpc server API currently does this to create Protobuf response.
class SampleServiceImpl final : public SampleService::Service
{
Status SampleAPI(ServerContext* context, const Request* request, Response* response) override
{ .......
//already has Protobuf serialized data"serialized_response_msg_buff"
proto::Response response;
any.ParseFromArray(serialized_response_msg_buff, serialized_response_msg_len);
any.UnpackTo(response);....
}
}
In my case, the Response object is very heavy and will end up doing deserialization twice, one in the above code and once by gRPC service when it sends to the client.
Is there a way to avoid the above deserialization and directly pass Protobuf serialized data to the gRPC client?

Related

GraphQL Subscriptions - Spring Boot Websocket Authentication

We are using the Netflix DGS framework to build our backend to provide a GraphQL API.
In addition to that we use Keykloak as an identity provider which comes with a handy Spring module to add support for authentication and authorization out of the box.
Every request contains a JWT token, which gets validated and from there a SecurityContext object is being generated which is then available in every endpoint.
This is working great for HTTP requests. GraphQL queries and mutations are sent via HTTP, therefore no problem here.
Subscriptions on the other hand use the web socket protocol. A WS request does not contain additional headers, therefore no JWT token is sent with the request.
We can add the token via a payload, the question is now how to set up a Spring Security Filter which creates a Security Context out of the payload.
I guess this is rather Spring specific, basically a filter which intercepts any web socket request (ws://... or wss://...) is needed.
Any help or hint is very much appreciated!
The only way to use headers in web socket messages is in the connection_init message. the headers will be sent by the client in the payload of the message.
The solution I propose is done in 2 steps (We will assume that the name of the header element is "token"):
Intercept the connection_init message, then force the insertion of a new element (token) in the subscription request.
Retrieve the element (token) of the header during the interception of the subscription and feed the context.
Concretely, the solution is the implementation of WebSocketGraphQlInterceptor interface
#Configuration
class SubscriptionInterceptor implements WebSocketGraphQlInterceptor {
#Override
public Mono<Object> handleConnectionInitialization(WebSocketSessionInfo sessionInfo, Map<String, Object> connectionInitPayload) {
sessionInfo.getHeaders().add("token", connectionInitPayload.get("token").toString());
return Mono.just(connectionInitPayload);
}
#Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
List<String> token = request.getHeaders().getOrEmpty("token");
return chain.next(request).contextWrite(context -> context. Put("token", token.isEmpty() ? "" : token.get(0)));
}
}

Transcode HTTP header into Grpc metadata for each request

I'am building an API-Gateway that proxies HTTP traffic to Grpc services. All incoming HTTP requests can have JWT in Authorization header. I need to transcode this JWT to Grpc metadata at each request and send it with Grpc request. I am using grpc-kotlin library with grpc code generator for kotlin suspend functions for client stub.
I have write this WebFilter to put header into ReactorContext:
#Component
class UserMetadataWebFilter : WebFilter {
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
exchange.request.headers[HttpHeaders.AUTHORIZATION]?.firstOrNull()?.let { authorizationHeader ->
return chain.filter(exchange).contextWrite { Context.of("myHeader", authorizationHeader) }
}
return chain.filter(exchange)
}
}
And it can be used in controller methods like this:
identityProviderClient.createUser(protobufRequest,
coroutineContext[ReactorContext]?.context?.get("myHeader") ?: Metadata())
I want to create Grpc client interceptor or something another to automaticly set Grpc metadata from coroutine context. I have many Grpc client calls and I believe that is to write this code for every call is not good practice.
I know about envoy-proxy, but I need apply specific logic to my requests, that's why envoy-proxy is not my choice.
How should I transcode Http header(s) into grpc client call metadata? Thanks.
ClientInterceptor seems appropriate. Intercept the channel, see utility function:
https://grpc.github.io/grpc-java/javadoc/io/grpc/ClientInterceptors.html#intercept-io.grpc.Channel-io.grpc.ClientInterceptor...-

rsocket routing metadata using RSocket-Java for Spring Rsocket Server

How do I setup routing metadata (in payload using just RSocket-Java when server is using Spring Boot Rsocket.
Flux<Payload> s = connection.flatMapMany(requester -> requester.requestStream(DefaultPayload.create("Some Message")))
Server is using #MessageMapping("/route")
Interaction type
RSocket interaction type on SpringBoot using #MessageMapping is decided based on signature of annotated method (more info in spring docs)
Let's assume it is having signature:
#MessageMapping("/route")
Flux<String> getStreamOfStrings(String message) {...}
Based on cardinality table from spring docs interaction type is Request-Stream.
RSocket client
RSocket java client needs to have specified mime-type for metadata:
RSocket rsocketClient = RSocketConnector.create()
//metadata header needs to be specified
.metadataMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.getString())
// value of spring.rsocket.server.port eg 7000
.connect(TcpClientTransport.create(7000))
.block();
Data
Data will be simple string:
ByteBuf data = ByteBufAllocator.DEFAULT.buffer().writeBytes("request msg".getBytes());
Metadata
Routing in RSocket is defined as metadata extension and needs to be sent together with data to specify routing. Here is example how it can be created (see other classes in package io.rsocket.metadata)
CompositeByteBuf metadata = ByteBufAllocator.DEFAULT.compositeBuffer();
RoutingMetadata routingMetadata = TaggingMetadataCodec.createRoutingMetadata(ByteBufAllocator.DEFAULT, List.of("/route"));
CompositeMetadataCodec.encodeAndAddMetadata(metadata,
ByteBufAllocator.DEFAULT,
WellKnownMimeType.MESSAGE_RSOCKET_ROUTING,
routingMetadata.getContent());
Request-stream request
Data and metadata are created so you can execute requestSteam using:
rsocketClient.requestStream(DefaultPayload.create(data, metadata))
.map(Payload::getDataUtf8)
.toIterable()
.forEach(System.out::println);

SpringBoot get InputStream and OutputStream from websocket

we want to integrate third party library(Eclipse XText LSP) into our SpringBoot webapp.
This library works "interactively" with the user (like chat). XText API requires input and output stream to work. We want to use WebSocket to let users interact with this library smoothly (send/retrieve json messages).
We have a problem with SpringBoot because SpringBoot support for WebSocket doesn't expose input/output streams. We wrote custom TextWebSocketHandler (subclass) but none of it's methods provide access to in/out streams.
We also tried with HandshakeInterceptor (to obtain in/out streams after handshake ) but with no success.
Can we use SpringBoot WebSocket API in this scenario or should we use some lower level (Servlet?) API ?
Regards Daniel
I am not sure if this will fit your architecture or not, but I have achieved this by using Spring Boot's STOMP support and wiring it into a custom org.eclipse.lsp4j.jsonrpc.RemoteEndpoint, rather than using a lower level API.
The approach was inspired by reading through the code provided in org.eclipse.lsp4j.launch.LSPLauncher.
JSON handler
Marhalling and unmarshalling the JSON needs to be done with the API provided with the xtext language server, rather than Jackson (which would be used by the Spring STOMP integration)
Map<String, JsonRpcMethod> supportedMethods = new LinkedHashMap<String, JsonRpcMethod>();
supportedMethods.putAll(ServiceEndpoints.getSupportedMethods(LanguageClient.class));
supportedMethods.putAll(languageServer.supportedMethods());
jsonHandler = new MessageJsonHandler(supportedMethods);
jsonHandler.setMethodProvider(remoteEndpoint);
Response / notifications
Responses and notifications are sent by a message consumer which is passed to the remoteEndpoint when constructed. The message must be marshalled by the jsonHandler so as to prevent Jackson doing it.
remoteEndpoint = new RemoteEndpoint(new MessageConsumer() {
#Override
public void consume(Message message) {
simpMessagingTemplate.convertAndSendToUser('user', '/lang/message',
jsonHandler.serialize(message));
}
}, ServiceEndpoints.toEndpoint(languageServer));
Requests
Requests can be received by using a #MessageMapping method that takes the whole #Payload as a String to avoid Jackson unmarshalling it. You can then unmarshall yourself and pass the message to the remoteEndpoint.
#MessageMapping("/lang/message")
public void incoming(#Payload String message) {
remoteEndpoint.consume(jsonHandler.parseMessage(message));
}
There may be a better way to do this, and I'll watch this question with interest, but this is an approach that I have found to work.

How to access previous SOAP headers in SoapInterceptor?

Here's my configuration:
A request is captured by my REST controller, I send data via SOAP to my webservice. Then I access some data sending SOAP request to another service and I return gathered data to the user sending the request.
Before sending SOAP request to external webservice I need to set some headers, so I have an interceptor that extends AbstractSoapInterceptor with Phase.PRE_PROTOCOL in constructor by webservice side.
Inside handleMessage() I create new headers and add to SoapMessage but... the data I need to set is inside REST request inside it's headers. So in order to get them I need to have access to HttpServletRequest and then I just get the header using HttpServletRequest#getHeader("header_name").
I've saw here I could just use #Context annotation but it's not available in my Spring (3.0.5) or maybe RESTEasy is something else and that question isn't connected with mine in anyway
I've tried this:
HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
and
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
but it's been a shot in the dark and it failed.
Edit:
Thanks to Abel ANEIROS I've added:
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
and was able to get HttpServletRequest from RequestContextHolder.getRequestAttributes() but apperently it's origin is from my webservice and not REST
Edit2:
The structure is different than I thought.
It's: Rest Controller -> MyWebService (SOAP) -> ExternalService (SOAP)
I've created another interceptor in controller side, added headers to SOAP message and now I'm trying to get the headers again in MyWebService side.

Resources