CORS with Crosswalk and OKHttp - android

I'm trying to use a crosswalk embedded webview to display a web page, with some javascript. Because I need to add some headers to each request, I am intercepting the request with shouldInterceptLoadRequest, and making the request with OkHttp.
#Override
public WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) {
try {
Log.i(App.TAG, url);
return new WebResourceResponse("", "UTF-8", getUrl(url));
} catch (Exception e) {
e.printStackTrace();
return super.shouldInterceptLoadRequest(view, url);
}
}
InputStream getUrl(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.addHeader("MyHeader","MyHeaderValue")
.build();
Response response = client.newCall(request).execute();
return response.body().byteStream();
}
This code works as intended at first, but upon making an Ajax request, I get this error : [INFO:CONSOLE(0)] "XMLHttpRequest cannot load https://api.example1.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://example2.com' is therefore not allowed access."
I do not get this error if I don't intercept the request, but then I loose the ability to add headers to the request.

Related

how to handle cookies for okhttpclient requests

I am sending a request to a website's webpage's url using OkHttpClient and storing the cookie it gives by the following method which I got from stackoverflow only.
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(this));
OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.build();
Request request = new Request.Builder()
.url("www.example.com/homePage")
.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0")
.build();
It is necessary to store the cookie otherwise the website redirects the request to a timeout page.
Then, I see the html of the page to check whether I reached the correct URL.
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
textView.setText("Failed to get response");
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
//String myResponse contains the html of the webpage
final String myResponse = response.body().string();
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText(myResponse);
}
});
}
}
});
Now, I make another Request variable in a similar fashion to the URL "www.example.com/loginPage" and try to see the html but it redirects me to the timeout page because the request is made without sending the cookie which I got from the homePage.
So, in short, how can I get a cookie from "www.example.com/cookieProviderPage" and store it and then use that cookie for all further requests to all pages of that website. Pages of that website have different urls like "www.example.com/cookieRequiredPage1", "www.example.com/cookieRequiredPage2" etc.
P.S. - I have checked many stackoverflow questions related to cookies but I am unable to implement the them specifically to my case.
Cookies added with Command
new OkHttpClient.Builder()
.cookieJar(cookieJar)
are loaded depending on the CookieJar.loadForRequest method. You must check, how your implementation of the interface CookieJar implemented the loadForRequest method. If cookies are just loaded for exact the same url the cookies originally come from, you have the error.
If the cookies come from "www.example.com/cookieProviderPage" and you want to supply the cookies for the request "www.example.com/loginPage", your CookieJar implementation may only supply the cookies to requests with the url "www.example.com/cookieProviderPage".
This is for example the case, if Cookie.matches is used to the get the cookies for the request. Cookie.matches compares domain name and path of the url.

Glide with aws cloudfront, throwing 403 error, but when i make request with okhttp i get proper response

Glide 4.10.0
When i try to load image into imageview with glide with custom cookie headers it throws 403 error at httpurlfetcher.java class while trying to read inputstream, but when i send the same request with okhttpclient i get proper response with response code 200, and even in browser i am able to view image.
In logs, i get file not found exception
java.io.FileNotFoundException: https://d2q89b5pewg0ry.cloudfront.net/images/hikup.jpg
But when i debug i get 403 in httpurlfetcher.java class
1.) Glide -> image is not loaded into imageview
List<String> cookies = Session.getInstance().getCookies(); GlideUrl glideUrl = new GlideUrl("https://d2q89b5pewg0ry.cloudfront.net/images/hikup.jpg", new LazyHeaders.Builder() .addHeader("Cookie", cookies.get(0)) .addHeader("Cookie", cookies.get(1)) .addHeader("Cookie", cookies.get(2)) .build()); Glide.with(this).load(glideUrl).error(android.R.color.white).into(profilePic);
2.) OkHttpClient -> here i get response
OkHttpClient client = new OkHttpClient(); final Request request = new Request.Builder() .addHeader("Cookie", cookies.get(0)) .addHeader("Cookie", cookies.get(1)) .addHeader("Cookie", cookies.get(2)) .url("https://d2q89b5pewg0ry.cloudfront.net/images/hikup.jpg") .build(); client.newCall(request).enqueue(new Callback() { #Override public void onFailure(Call call, IOException e) { runOnUiThread(() -> { Toast.makeText(ProfileActivity.this,e.toString(),Toast.LENGTH_LONG).show(); }); } #Override public void onResponse(Call call, final okhttp3.Response response) { runOnUiThread(() -> { Toast.makeText(ProfileActivity.this, response.toString(), Toast.LENGTH_LONG).show(); }); } });
When i debug loadDataWithRedirects method in httpurlfetcher.java class
urlConnection = connectionFactory.build(url);
// here i put a breakpoint and evaluate urlConnection.getResponseCode(), i get 403 how am i getting responsecode even before connection? headers are added in next line?
for (Map.Entry<String, String> headerEntry : headers.entrySet()) { urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue()); } urlConnection.setConnectTimeout(timeout); urlConnection.setReadTimeout(timeout); urlConnection.setUseCaches(false); urlConnection.setDoInput(true); // Stop the urlConnection instance of HttpUrlConnection from following redirects so that // redirects will be handled by recursive calls to this method, loadDataWithRedirects. urlConnection.setInstanceFollowRedirects(false); // Connect explicitly to avoid errors in decoders if connection fails. urlConnection.connect(); // Set the stream so that it's closed in cleanup to avoid resource leaks. See #2352. stream = urlConnection.getInputStream();
Issue has been resolved, i used okhttp integration library with interceptor to set headers. Issue was with lazy headers, when same key exists, in my case "Cookie", all the headers with that key get combined, whereas AWS Cloudfront needs 3 separate Cookie headers.

Getting around X-Frame-Options DENY in an Android WebView

I am attempting to implement a technique similar to the one describe in this question.
I have an android application (Ionic built on top of Cordova) that runs in a webview. Basically what I want to do is load a page into an iframe and perform some work on this page. Many website uses the X-Frame-Options: DENY header to disallow their content from being loaded in an iFrame. In a chrome extension you can get around this by intercepting the webrequest and removing that header.
I've overridden the shouldInterceptRequest function here: https://developer.android.com/reference/android/webkit/WebViewClient.html
// Handle API until level 21
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
try {
WebResourceResponse cordovaResponse = super.shouldInterceptRequest(view, request);
if(cordovaResponse != null) {
return cordovaResponse;
}
String url = request.getUrl().toString();
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.connect();
//view.loadUrl(url, getCustomHeaders());
WebResourceResponse response = new WebResourceResponse(urlConnection.getContentType(),
urlConnection.getContentEncoding(),
urlConnection.getInputStream());
Map<String, String> headers = response.getResponseHeaders();
if(headers != null){
response.setResponseHeaders(removeXOriginHeaders(headers));
}
return response;
} catch(MalformedURLException e) {
e.printStackTrace();
return null;
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}`
but when the headers for all requests are received using the above method they are null and when the content is put into the iframe, it doesn't result in a fully formed Document.
The chrome debugger provides this message: Resource interpreted as Document but transferred with MIME type text/html;charset=UTF-8:
It's like the page content is fetched using xhr and then stuck inside a single element of the Document as opposed to loading as it normally would when using an iframe (all scripts run to execution, subsequent ajax requests fired etc).
Is there anyway to get the page content to load in the iframe after having removed that single header?
I was able to solve my problem by using the OkHttpClient found here: http://square.github.io/okhttp/ instead of the java URLConnection
// Handle API until level 21
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
try {
WebResourceResponse cordovaResponse = super.shouldInterceptRequest(view, request);
if(cordovaResponse != null) {
return cordovaResponse;
}
String url = request.getUrl().toString();
OkHttpClient httpClient = new OkHttpClient();
Request okRequest = new Request.Builder()
.url(url)
.build();
Response response = httpClient.newCall(okRequest).execute();
Response modifiedResponse = response.newBuilder()
.removeHeader("x-frame-options")
.removeHeader("frame-options")
.build();
return new WebResourceResponse("text/html",
modifiedResponse.header("content-encoding", "utf-8"),
modifiedResponse.body().byteStream()
);
} catch(MalformedURLException e) {
e.printStackTrace();
return null;
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}

how to contain header when I redirect in retrofit

I tried to make oauth2 for android application. it has little bug.
My bug is It doesn't have header like Authorization when I redirect
MyCookieCode. It send Authorization when I was login. but It doesn't work when I redirect
public static Retrofit getLoginRetrofitOnAuthz() {
Retrofit.Builder builder = new Retrofit.Builder().baseUrl(ServerValue.AuthServerUrl).addConverterFactory(GsonConverterFactory.create());
if (LoginRetrofitAuthz == null) {
httpClientAuthz.addInterceptor(new Interceptor() {
#Override
public okhttp3.Response intercept(Chain chain) throws IOException {
String str = etUsername.getText().toString() + ":" + etPassword.getText().toString();
String Base64Str = "Basic " + Base64.encodeToString(str.getBytes(), Base64.NO_WRAP);
System.out.println(Base64Str);
Request request = chain.request().newBuilder().addHeader("Authorization", Base64Str).build();
return chain.proceed(request);
}
});
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
httpClientAuthz.cookieJar(new JavaNetCookieJar(cookieManager));
LoginRetrofitAuthz = builder.client(httpClientAuthz.build()).build();
}
return LoginRetrofitAuthz;
}
Server Result (Top-Login, Bottom, Redirect)
Do you know how to staying header on redirect ?
in fact the sinner is OkHttp, but not Retrofit.
OkHttp removes all authentication headers on purpose:
https://github.com/square/okhttp/blob/7cf6363662c7793c7694c8da0641be0508e04241/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java
// When redirecting across hosts, drop all authentication headers. This
// is potentially annoying to the application layer since they have no
// way to retain them.
if (!sameConnection(url)) {
requestBuilder.removeHeader("Authorization");
}
Here is the discussion of this issue: https://github.com/square/retrofit/issues/977
You could use the OkHttp authenticator. It will get called if there is a 401 error returned. So you could use it to re-authenticate the request.
httpClient.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
return response.request().newBuilder()
.header("Authorization", "Token " + DataManager.getInstance().getPreferencesManager().getAuthToken())
.build();
}
});
However in my case server returned 403 Forbidden instead of 401. And I had to get
response.headers().get("Location");
in-place and create and fire another network request:
public Call<Response> getMoreBills(#Header("Authorization") String authorization, #Url String nextPage)

Android - Webview only applying headers to initial request

I'm writing an android app that uses webview to request content from a web server, but using mWebView.loadUrl(url1, headers); will only apply the headers to the initial request and not the resources in the request.
Any idea as so how to apply the headers to the resource requests as well?
Not absolutely sure but you can try to override shouldOverrideUrlLoading(WebView view, String url) method and handle all redirects by starting mWebView.loadUrl(url, yourHeaders);
Dont forget to return true in that overriden method.
First of all, let me say that i can't believe that webview sucks so much.
This is what i did to pass custom headers
public class CustomWebview extends WebView {
public void loadWithHeaders(String url) {
setWebViewClient(new WebViewClient() {
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
//makes a custom http request, which allows you to add your own headers
return customRequest(url);
}
});
loadUrl(url);
}
/**
* Custom http request with headers
* #param url
* #return
*/
private WebResourceResponse customRequest(String url) {
try {
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url.trim())
.addHeader("Header-Name", "Android Sucks")
.build();
Response response = httpClient.newCall(request).execute();
return new WebResourceResponse(
"text/html", // You can set something other as default content-type
"utf-8", // Again, you can set another encoding as default
response.body().byteStream()
);
} catch (IOException e) {
//return null to tell WebView we failed to fetch it WebView should try again.
return null;
}
}
}

Categories

Resources