How to set header response in grpc java server - grpc

#Override
public void getResult(Request request, StreamObserver<Response> responseObserver) {
responseObserver.onNext(new Respons("check"));
responseObserver.onCompleted();
}
how to send header response?
In golang I can send it by doing like this but in java I don't know
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
// create and send header
header := metadata.Pairs("header-key", "val")
grpc.SendHeader(ctx, header)
// create and set trailer
trailer := metadata.Pairs("trailer-key", "val")
grpc.SetTrailer(ctx, trailer)
}

You can implemente ServerInterceptor and modify header in sendHeaders method like this:
public class CustomServerInterceptor implements ServerInterceptor {
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
CustomServerCall<ReqT, RespT> customServerCall = new CustomServerCall<>(call);
ServerCall.Listener<ReqT> listener = next.startCall(customServerCall, headers);
return new CustomServerCallListener<>(listener);
}
}
class CustomServerCall<ReqT, RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
// ...
#Override
public void sendHeaders(Metadata headers) {
// do what you want here
super.sendHeaders(headers);
}
}
class CustomServerCallListener<ReqT> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
// ...
}

Related

Static headers and dynamic headers issue in retrofit

I need help regarding the Java Retrofit request:
Scenario 1: I have added an interceptor having few static headers.
Scenario 2: While requesting API, sending few dynamic headers also.
When the request completes, I check request headers like below.
response.raw().request().headers()
where I can see the static headers but not the dynamic headers.
below is the code for Interceptor to set static headers:
public class AuthInterceptor implements Interceptor {
public AuthInterceptor() {
}
protected String authtoken;
public AuthInterceptor(String authtoken) {
defaultHeader();
this.authtoken = authtoken;
}
public void setAuthtoken(String authtoken) {
this.authtoken = authtoken;
}
private Headers.Builder defaultHeader() {
final String xUserAgent = Util.SDK_NAME + "/" + Util.SDK_VERSION;
return new Headers.Builder()
.add("X-User-Agent", xUserAgent)
.add("User-Agent", Util.defaultUserAgent())
.add("Content-Type", "application/json");
}
public Headers.Builder addHeader(#NotNull String key, #NotNull String value) {
defaultHeader().add(key, value);
return defaultHeader();
}
#NotNull
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder request = chain.request().newBuilder()
.headers(defaultHeader().build());
if (this.authtoken != null) {
request.addHeader("authtoken", this.authtoken);
}
return chain.proceed(request.build());
}
}
And Sending dynamic headers like below.
#POST("stacks")
Call<ResponseBody> create(
#Header("organization_uid") String orgUid,
#Body RequestBody body);
It looks to me like the problem is in your use of:
Request.Builder request = chain.request().newBuilder()
.headers(defaultHeader().build());
If you look at the documentation of the 'headers' method it states: Removes all headers on this builder and adds {#code headers}.
Just add each header with addHeader and you should be fine.

How to pass data from grpc rpc call to server interceptor in java

I am trying to set some metadata with a value from the response after the rpc server call has been processed. The plan was to use server interceptor and override close method.
Something like this: https://github.com/dconnelly/grpc-error-example/blob/master/src/main/java/example/Errors.java#L38
Since the metadata value depends on the response, I need some way to pass data from rpc server call to server interceptor or access the response from interceptor
In Golang, the metadata can be set easily in the rpc call grpc.SetTrailer after processing but in java there is no way to do it in rpc call. So I am trying to use server interceptor for the same.
Can someone help?
You can use grpc-java's Contexts for that.
In the interceptor you attach a Context with a custom key containing a mutable reference. Then in the call you access that header again and extract the value from it.
public static final Context.Key<TrailerHolder> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new TrailerHolder());
Context previousContext = context.attach();
[...]
context.detach(previousContext);
You can access the context value like this:
TrailerHolder trailerHolder = TRAILER_HOLDER_KEY.get();
You might want to implement your code similar to this method:
Contexts#interceptCall(Context, ServerCall, Metadata, ServerCallHandler)
EDIT:
import io.grpc.Context;
import io.grpc.ForwardingServerCall.SimpleForwardingServerCall;
import io.grpc.ForwardingServerCallListener;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCall.Listener;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import io.grpc.Status;
public class TrailerServerInterceptor implements ServerInterceptor {
public static final Context.Key<Metadata> TRAILER_HOLDER_KEY = Context.key("trailerHolder");
#Override
public <ReqT, RespT> Listener<ReqT> interceptCall(final ServerCall<ReqT, RespT> call, final Metadata headers,
final ServerCallHandler<ReqT, RespT> next) {
final TrailerCall<ReqT, RespT> call2 = new TrailerCall<>(call);
final Context context = Context.current().withValue(TRAILER_HOLDER_KEY, new Metadata());
final Context previousContext = context.attach();
try {
return new TrailerListener<>(next.startCall(call2, headers), context);
} finally {
context.detach(previousContext);
}
}
private class TrailerCall<ReqT, RespT> extends SimpleForwardingServerCall<ReqT, RespT> {
public TrailerCall(final ServerCall<ReqT, RespT> delegate) {
super(delegate);
}
#Override
public void close(final Status status, final Metadata trailers) {
trailers.merge(TRAILER_HOLDER_KEY.get());
super.close(status, trailers);
}
}
private class TrailerListener<ReqT> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT> {
private final Context context;
public TrailerListener(final ServerCall.Listener<ReqT> delegate, final Context context) {
super(delegate);
this.context = context;
}
#Override
public void onMessage(final ReqT message) {
final Context previous = this.context.attach();
try {
super.onMessage(message);
} finally {
this.context.detach(previous);
}
}
#Override
public void onHalfClose() {
final Context previous = this.context.attach();
try {
super.onHalfClose();
} finally {
this.context.detach(previous);
}
}
#Override
public void onCancel() {
final Context previous = this.context.attach();
try {
super.onCancel();
} finally {
this.context.detach(previous);
}
}
#Override
public void onComplete() {
final Context previous = this.context.attach();
try {
super.onComplete();
} finally {
this.context.detach(previous);
}
}
#Override
public void onReady() {
final Context previous = this.context.attach();
try {
super.onReady();
} finally {
this.context.detach(previous);
}
}
}
}
In your grpc service method you can simply use TRAILER_HOLDER_KEY.get().put(...)

Spring Security OAuth2 - How to modify token response JSON?

I am getting the Response JSON (for JWT token request) as below:
{
"access_token": "<JWT Access Token>",
"token_type": "bearer",
"refresh_token": "<JWT Refresh Token>",
"expires_in": 3599,
"scope": "read write trust",
"DateOfBirth": "01-01-1990",
"Address_Line_1": "ABCD Andrews Dr, Apt 111",
"PAN_Number": "12345ABCD",
"Address_Line_2": "Dublin, CA 94588",
"jti": "e6a19730-e4e5-4cec-bf59-bd90ca1acc34"
}
I want to modify it (by removing a few elements) to:
{
"access_token": "<JWT Access Token>",
"token_type": "bearer",
"refresh_token": "<JWT Refresh Token>",
"expires_in": 3599,
"scope": "read write trust",
"jti": "e6a19730-e4e5-4cec-bf59-bd90ca1acc34"
}
I tried to used ResponseBodyAdvice as adviced by a few. But issue is response body object (available as public Object beforeBodyWrite(Object body ...) is of object type - "org.springframework.security.oauth2.common.DefaultOAuth2AccessToken" and not JSON. I am not sure how i can manipulate DefaultOAuth2AccessToken to remove the additional elements.
Could anybody please help me?
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIEN_ID)
.secret(passwordEncoder().encode(CLIENT_SECRET))
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, REFRESH_TOKEN)
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
public class CustomTokenEnhancer implements TokenEnhancer {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("DateOfBirth", oAuth2Authentication.getOAuth2Request().getRequestParameters().get("dob"));
additionalInfo.put("PAN_Number", oAuth2Authentication.getOAuth2Request().getRequestParameters().get("pan"));
additionalInfo.put("Address_Line_1", oAuth2Authentication.getOAuth2Request().getRequestParameters().get("addr1"));
additionalInfo.put("Address_Line_2", oAuth2Authentication.getOAuth2Request().getRequestParameters().get("addr2"));
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionalInfo);
return oAuth2AccessToken;
}
}
#ControllerAdvice
public class ResponseJSONAdvice implements ResponseBodyAdvice<Object> {
#Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends
HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
/*
Logic to remove additional elements from response JSON.
But Object body is of type org.springframework.security.oauth2.common.DefaultOAuth2AccessToken and not JSON!!
*/
return body;
}
}
Keep using ResponseBodyAdvice, first define a class which have all field you want to show. Then make method beforeBodyWrite return that class. In beforeBodyWrite method you want to set field of defined class by value of body, then return it.
Sorry i don't good at english, ask me if u don't get it ;)
public BaseResponse beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
BaseResponse res = new BaseResponse();
res.setResponseStatusCode(StatusResponse.SUCCESS.getCode());
res.setResponseStatusMessage(StatusResponse.SUCCESS.getName());
res.setContent(body);
return res;
}
From my point of view you have to adjust your token enhance that you configure in your AuthorizationServerConfigurerAdapter
in the method
public void configure(
AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
you can set your custom enhance or configure one. This should allow you to turn on or off what is send with the token
It worked for me:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter(), tokenEnhancer()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}

Retrofit Observables and access to Response

I'm using Retrofit and RxJava but can't seem to do what I want.
Here's my declaration of my web service:
Observable<Response> rawRemoteDownload(#Header("Cookie") String token, #Path("programId") int programId);
The problem I have is the webservice is returning a 403 and a json payload with details.
Retrofit calls onError, only passing the Throwable so I can't check the response body.
Here's part of my test code
apiManager.rawRemoteDownloadRequest("token", 1).subscribe(new Observer<Response>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
// this is called and I've lost the response!
}
#Override
public void onNext(Response response) {
}
});
SOLUTION:
Thanks to Gomino, I went with this as a solution:
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
if (throwable instanceof RetrofitError) {
Response response = ((RetrofitError) throwable).getResponse();
System.out.println(convertToString(response.getBody()));
}
}
where convertToString looks like:
private String convertToString(TypedInput body) {
byte[] bodyBytes = ((TypedByteArray) body).getBytes();
return new String(bodyBytes);
}
Check if the throwable is a RetrofitError:
#Override
public void onError(Throwable e) {
if (e instanceof RetrofitError) {
Response response = ((RetrofitError) e).getResponse();
}
}

Httpclien 4 gzip Post-Data

i'm using httpclient 4. When i use
new DecompressingHttpClient(client).execute(method)
the client acccepts gzip and decompresses if the server sends gzip.
But how can i archieve that the client sends it's data gzipped?
HttpClient 4.3 APIs:
HttpEntity entity = EntityBuilder.create()
.setText("some text")
.setContentType(ContentType.TEXT_PLAIN)
.gzipCompress()
.build();
HttpClient 4.2 APIs:
HttpEntity entity = new GzipCompressingEntity(
new StringEntity("some text", ContentType.TEXT_PLAIN));
GzipCompressingEntity implementation:
public class GzipCompressingEntity extends HttpEntityWrapper {
private static final String GZIP_CODEC = "gzip";
public GzipCompressingEntity(final HttpEntity entity) {
super(entity);
}
#Override
public Header getContentEncoding() {
return new BasicHeader(HTTP.CONTENT_ENCODING, GZIP_CODEC);
}
#Override
public long getContentLength() {
return -1;
}
#Override
public boolean isChunked() {
// force content chunking
return true;
}
#Override
public InputStream getContent() throws IOException {
throw new UnsupportedOperationException();
}
#Override
public void writeTo(final OutputStream outstream) throws IOException {
final GZIPOutputStream gzip = new GZIPOutputStream(outstream);
try {
wrappedEntity.writeTo(gzip);
} finally {
gzip.close();
}
}
}

Resources