I am trying to add a header to HTTP responses. This is my Interceptor which is not working:
builder.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request addedTestRequest = request.newBuilder().addHeader("TEST", "test").build();
Response response = chain.proceed(addedTestRequest);
final Response addedTestResponse = response.newBuilder().addHeader("TEST", "test").build();
return addedTestResponse;
}
});
I can see the header TEST in outgoing traffic (requests) but not in incoming traffic (responses). Do you know what is the problem or is there another way to add headers to responses?
Note: I am using franmontiel/PersistentCookieJar with OkHttp3 / Retrofit, if that influences anything.
I suspect you’re logging the response before your additional headers are added. If you reorder the interceptors you’ll most likely see the response header where you expect it, but you’ll lose the request header.
Looking at this overview might help you to understand which interceptors see which values.
The request headers and response headers are different. I'm not familiar with that cookie store but this test explain how to use cookies in okHtttp.
https://github.com/square/okhttp/blob/c581f5ddc6a091e36e745a44ca787d903e32df51/okhttp-tests/src/test/java/okhttp3/CookiesTest.java#L78
You can simply add two interceptors into your OkHttp instance, which will do next operations:
scan cookies from the responses;
save found cookies into the prefs;
attach cookies from the prefs into the requests;
Of course, you can customize this logic according to your tasks.
public class AddCookiesInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder builder = chain.request().newBuilder();
HashSet<String> preferences = (HashSet) Preferences.getDefaultPreferences().getStringSet(Preferences.PREF_COOKIES, new HashSet<>());
for (String cookie : preferences) {
builder.addHeader("Cookie", cookie);
}
return chain.proceed(builder.build());
}
}
public class ReceivedCookiesInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
HashSet<String> cookies = new HashSet<>();
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
}
Preferences.getDefaultPreferences().edit()
.putStringSet(Preferences.PREF_COOKIES, cookies)
.apply();
}
return originalResponse;
}
}
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.interceptors().add(new AddCookiesInterceptor());
okHttpClient.interceptors().add(new ReceivedCookiesInterceptor());
Related
I'm working on a POC i need to use zuul as a sever to route 2 routes first will run normally but it has a custom post filter which will send another request to other api using some data of the response of the first requet,
so need to extract the response body of the first request into my custom post filter and get some specific attributes but i can not find the response as it always be null but the status code is 200.
how can i wait and get a value of specific attribute from the response and get the actual status code not just 200 as default value.
i tried to make this implementation using cloud gateway but i reached the same point of disability of extracting the response.
also i tried to make a response decorator but it failed too.
#Component
public class AddResponseHeaderFilter extends ZuulFilter {
#Override
public String filterType() {
return "post";
}
#Override
public int filterOrder() {
return 1;
}
#Override
public boolean shouldFilter() {
return true;
}
#Override
public Object run() {
System.out.println("this is my filter");
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = new HttpServletRequestWrapper(context.getRequest());
System.out.println(String.format("%s request to %s", request.getMethod(), request.getRequestURL().toString()));
HttpServletResponse servletResponse = context.getResponse();
// return an address only
System.out.println(context.getResponseBody().toString());
servletResponse.addHeader("X-Foo", UUID.randomUUID().toString());
return null;
}
}
RequestContext.getCurrentContext().getResponseDataStream() works fine for me, I am also able to manipulate the response.
import java.nio.charset.Charset;
import org.springframework.util.StreamUtils;
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String requestLog = StreamUtils.copyToString(request.getInputStream(),
Charset.forName("UTF-8"));
Problem Statement:
I'm using Retrofit in my application for API calls. Currently I've 20+ Retrofit Interfaces, with different Callbacks. Currently when app receives INVALID_SESSION_ID in anyone of these Interfaces (say UpdateUserAPI), I've to get new ACCESS_TOKEN, by invoking AccessTokenAPI.
Approach Suggested:
When app receives INVALID_SESSION_ID in Callback in UpdateUserAPI, invoke AccessTokenAPI to get new ACCESS_TOKEN. Upon receiving new ACCESS_TOKEN, post the actual call (with initial parameters in UpdateUserAPI) with new ACCESS_TOKEN. But this requires to save parameters in the class which implements UpdateUserAPI. Also I need to retry getting ACCESS_TOKEN only once, which should be handled.
What is the best approach to implement above requirement?
Create your own TokenInterceptor
public class TokenInterceptor implements Interceptor
Then set it to your okktpclient
Interceptor tokenInterceptor = new TokenInterceptor(provideUserLoginDao(appDatabase));
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(tokenInterceptor)
.writeTimeout(50, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.build();
Useful information in this post also : Refreshing OAuth token using Retrofit without modifying all calls
Create your own custom interceptor and check your token/session_id is valid or not. If your session_id is expired and then hit your updateUserAPI to get new id and set this id in header or where you want. Here is some code samples.
RefreshTokenInterceptor
public static class RInterceptor implements Interceptor {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
try {
if (response.code() == 410) {
Response r = null;
try {
r = makeTokenRefreshCall(request, chain);
} catch (JSONException e) {
e.printStackTrace();
}
return r;
}
} catch (Exception e) {
e.printStackTrace();
}
return response;
}
}
private static Response makeTokenRefreshCall(Request req, Interceptor.Chain chain) throws JSONException, IOException {
/* fetch refreshed token, some synchronous API call, whatever Because we are responsible to return new response */
refreshTokenSync();
Request newRequest;
newRequest = req.newBuilder().header("authorization", NEW_TOKEN)/*.post(req.body())*/.build();
return chain.proceed(newRequest);
}
RESTClient
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(50, TimeUnit.SECONDS)
.writeTimeout(55, TimeUnit.SECONDS)
.connectTimeout(50, TimeUnit.SECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(new NetworkInterceptor())
.build();
I've been debugging this for three hours, I still cannot explain why my custom headers (registered via a client request filter) are not sent.
The client is configured as such (full source here):
private WebTarget webTarget(String host, String appId, String appKey) {
return newClient(clientConfiguration(appId, appKey))
.target(host + "/rest");
}
private Configuration clientConfiguration(String appId, String appKey) {
ClientConfig config = new ClientConfig();
config.register(requestFilter(appId, appKey));
return config;
}
private ClientRequestFilter requestFilter(String appId, String appKey) {
return new VidalRequestFilter(apiCredentials(appId, appKey));
}
The filter is as follows:
public class VidalRequestFilter implements ClientRequestFilter {
private final ApiCredentials credentials;
public VidalRequestFilter(ApiCredentials credentials) {
this.credentials = credentials;
}
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
MultivaluedMap<String, Object> headers = requestContext.getHeaders();
headers.add(ACCEPT, APPLICATION_ATOM_XML_TYPE);
headers.add("app_id", credentials.getApplicationId());
headers.add("app_key", credentials.getApplicationKey());
}
}
And the call is like:
String response = webTarget
.path("api/packages")
.request()
.get()
.readEntity(String.class);
All I get is 403 forbidden, because the specific endpoint I am calling is protected (the auth is performed with the custom headers defined above).
The weirdest thing is that, while I'm debugging, I see that sun.net.www.MessageHeader is properly invoked during the request write (i.e. the instance is valued as such: sun.net.www.MessageHeader#14f9390f7 pairs: {GET /rest/api/packages HTTP/1.1: null}{Accept: application/atom+xml}{app_id: XXX}{app_key: YYY}{User-Agent: Jersey/2.22.1 (HttpUrlConnection 1.8.0_45)}{Host: ZZZ}{Connection: keep-alive}.
However, I have the confirmation that neither our API server, nor its reverse proxy received GET requests with the required auth headers (a first HEAD request seems to be OK, though).
I know for sure the credentials are good 'cause the equivalent curl command just works!
I tried the straightforward approach to set headers directly when defining the call without any success.
What am I missing?
I need to retry request inside of OkHttp Interceptor. For example there is incoming request which needs Authorization token. If Authorization token is expired, server returns response with 403 code. In this case I am retrieving a new token and trying to make call again by using the same chain object.
But OkHttp throws an exception, which states that you cannot make two requests with the same chain object.
java.lang.IllegalStateException: network interceptor org.app.api.modules.ApplicationApiHeaders#559da2 must call proceed() exactly once
I wonder if there is a clean solution to this problem of retrying network request inside of OkHttp Interceptor?
public final class ApplicationApiHeaders implements Interceptor {
private static final String AUTHORIZATION = "Authorization";
private TokenProvider mProvider;
public ApplicationApiHeaders(TokenProvider provider) {
mProvider = provider;
}
#Override
public Response intercept(Chain chain) throws IOException {
Token token = mProvider.getApplicationToken();
String bearerToken = "Bearer " + token.getAccessToken();
System.out.println("Token: " + bearerToken);
Request request = chain.request();
request = request.newBuilder()
.addHeader(AUTHORIZATION, bearerToken)
.build();
Response response = chain.proceed(request);
if (!response.isSuccessful() && isForbidden(response.code())) {
Token freshToken = mProvider.invalidateAppTokenAndGetNew();
String freshBearerToken = freshToken.getAccessToken();
Request newRequest = chain.request();
newRequest = newRequest.newBuilder()
.addHeader(AUTHORIZATION, freshBearerToken)
.build();
response = chain.proceed(newRequest);
}
return response;
}
private static boolean isForbidden(int code) {
return code == HttpURLConnection.HTTP_FORBIDDEN;
}
}
Use .interceptors() instead of .networkInterceptors() which are allowed to call .proceed() more than once.
For more information see: https://square.github.io/okhttp/interceptors/
I need to apply an Authorization header in a request interceptor, but I need to sign the request method, URI, and date.
Inside the request interceptor I get a RequestInterceptor.RequestFacade, which only has "setter methods"
Is there any way I can get request properties inside a request interceptor?
Ah, did some more googling. The way to do this is to use a client wrapper. Observe...
public class SigningClient implements Client {
final Client wrapped;
public SigningClient(Client client) {
wrapped = client;
}
#Override public Response execute(Request request) {
Request newRequest = sign(request);
return wrapped.execute(newRequest);
}
private void sign(Request request) {
// magic
}
}
Found it here: https://github.com/square/retrofit/issues/185#issuecomment-17819547