Android WebView custom headers - java

I am currently using this code to add a custom header to android WebView
Map<String, String> extraHeaders = new HashMap<String, String>();
extraHeaders.put("example", "header");
webView.loadUrl(url, extraHeader);
Above code is working but only on the main page. So if I write this code echo $_SERVER['example'] it prints header. But there is an iframe in the loaded URL which shows an undefined error when I try the same code. Is there any way I can fix this?
So what I want to do is add custom header not only to the main loaded URL but also on the iframe of the loaded page.

This worked for me:
mWebView.setWebViewClient(new MyWebViewClient(token));
in MyWebViewClient
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
try {
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url.trim())
.addHeader("token", mToken) //add headers
.build();
Response response = httpClient.newCall(request).execute();
return new WebResourceResponse(
getMimeType(url), // set content-type
response.header("content-encoding", "utf-8"),
response.body().byteStream()
);
} catch (IOException e) {
return null;
}
}
//get mime type by url
public String getMimeType(String url) {
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
if (extension.equals("js")) {
return "text/javascript";
}
else if (extension.equals("woff")) {
return "application/font-woff";
}
else if (extension.equals("woff2")) {
return "application/font-woff2";
}
else if (extension.equals("ttf")) {
return "application/x-font-ttf";
}
else if (extension.equals("eot")) {
return "application/vnd.ms-fontobject";
}
else if (extension.equals("svg")) {
return "image/svg+xml";
}
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}

You can put this setting to your web setting, every request from webview will be using this User-Agent header.
webview.getSettings().setUserAgentString("user-agent-string");

No, that is not possible with Android WebView itself. You have to work around either in your page code, or in your app's code, or on the server.
For fixing this on the page's side, you can use XMLHttpRequest for loading subresources. But for that you will have basically to construct the page on the fly.
On the app's side, you can use WebViewClient.shouldInterceptRequest, to intercept all the network requests. You are not allowed to just modify the provided request, instead, you will need to make a new request yourself, but there you will be able to set any headers you want. See this example: Android WebViewClient url redirection (Android URL loading system)
On the server side, you can look into Referer header of subresources, which must contain the url of the page that has requested it.

Just add this piece of code before load URL:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().removeAllCookies(null);
}

Related

Extract HTML content from a POST webview - Java

I am trying to extract the HTML content from a Webview. I found interesting subject on stackoverflow, but all of these answers loads the URL in order to get the HTML content. Here, I need to extract the HTML content of a webpage that has been generated from a POST method.
Using, the java method below, the HTML content loaded will just be (because it loads the url within the method, instead of directly extracting the html content from the webview)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>400 Bad Request</title>
<h1>Bad Request</h1>
<p>The browser (or proxy) sent a request that this server could not understand.</p>
private static class RetrieveHTML extends AsyncTask<String, String, String> {
private static String htmlContent;
protected String doInBackground(String... url) {
return getRemoteContent(url[0]);
}
protected void onProgressUpdate(Integer... progress) {
}
protected void onPostExecute(Long result) {
}
private static String getRemoteContent(String url)
{
HttpPost pageGet = new HttpPost(url);
HttpClient client = new DefaultHttpClient();
ResponseHandler<String> handler = new ResponseHandler<String>()
{
public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException
{
HttpEntity entity = response.getEntity();
String html;
if (entity != null)
{
html = EntityUtils.toString(entity);
return html;
}
else
{
return null;
}
}
};
String pageHTML = null;
try
{
pageHTML = client.execute(pageGet, handler);
//if you want to manage http sessions then you have to add localContext as a third argument to this method and have uncomment below line to sync cookies.
//syncCookies();
}
catch (Exception e)
{
e.printStackTrace();
}
// you can filter your html content here if you wish before displaying
// in webview
try {
Log.d("TEST", pageHTML);
}
catch (Exception e){
e.printStackTrace();
}
htmlContent = pageHTML;
return pageHTML;
}
}
Thanks in advance
EDIT : I forgot to say why I am trying to do this :
I am adapting a Desktop website into an android application (mostly showing webview of mobile templates). I have a map into my desktop website, and markers are placed on it (those markers are transmitted via a json String through Flask+jinja). I got the idea to hide thoses markers in an html hidden tag. I could then extract the html and then parse the right part of this html content in order to get this json string into my java application (and then, use google-maps method thats exists in android studio)
Finally I decided to do what I wanted by an other way.
Everytime I do this post request, i generate a temp html file in which I write all the information that I need to get within my Java Application. I can then call this page from java (using the method above) because there is no data to re-send (since it is not a post-generated page)

Play framework 2.6 - Java : Ws request POST with oAuth Instagram

I'm trying to communicate with Instagram's API but the reply I get back from my request says that the parameters I passed onto the body weren't detected.
{"error_type":"OAuthException","code":400,"error_message":"You must provide a client_id"}
I tried to send the request by passing a JsonNode or a string inside .post(), like below, but both where unsuccessful.
public CompletionStage<Result> getInstagramToken() {
String code = request().getQueryString("code");
if(code != null) {
WSRequest request = ws.url("https://api.instagram.com/oauth/access_token").setContentType("application/x-wwww-form-urlencoded");
// Json body
/*JsonNode body = Json.newObject()
.put("client_id", insta_clientId)
.put("client_secret", insta_clientSecret)
.put("grant_type", "authorization_code")
.put("redirect_uri", redirect_uri)
.put("code", code);*/
// String body
String body = "client_id="+insta_clientId+"&client_secret="+insta_clientSecret+"&grant_type=authorization_code&redirect_uri="+redirect_uri+"&code="+code;
CompletionStage<WSResponse> response = request.post(body);
return response.thenApplyAsync(resp -> ok(resp.asJson()), exec);
}
return null;
}
The same request passed flawlessly when trying to send it by using a curl command on a terminal or with the Rested plugin on chrome ( where "content type" is set to "application/x-www-form-urlencoded" and the parameters are placed inside "Request body" )
Does anyone have any idea as to how I am supposed to send this request ?
ps: I am also looking for a way to retrieve the value received from my request and store it in a variable instead of returning it to the client.
It seems you are missing a:
.setContentType("application/x-www-form-urlencoded")
Look at our code below. In the post() you can also use a Json object so you can send a HashMap:
CompletionStage<Result> out = ws.url(cbUrl)
.setAuth(<<your user>> , <<your password>>, WSAuthScheme.BASIC)
.setRequestTimeout(Duration.ofSeconds(5))
.setContentType("application/x-www-form-urlencoded")
.post("param=value")
.handle((response, error) -> {
// Was the Chargebee API successful?
if (error == null) {
// Debugging purposes
JsonNode jn = response.asJson();
Logger.debug(Json.toJson(postMap).toString());
Logger.debug(jn.toString());
// Success stuff
return ok("good");
} else {
// Error stuff
return ok("bad");
}
});
Hope this helps you.

Why don't I receive POST vars from OkHttp3? (Android Studio)

I've answered my own question here. See the first code block where I use php://input to get the posted data.
I'm trying to send a post request from my app to a webserver, and I'm checking for the post vars using PHP:
if( isset( $_POST['name'] ) ){
echo json_encode(['status' => 1]);
}else if( $posted_data = (string) #file_get_contents('php://input') ) {
echo json_encode(['status' => 2]);
}else{
echo json_encode($_POST);
}
The request always returns and empty json encoded array.
I'm using the latest Android Studio, and the latest OkHttp, com.squareup.okhttp3:okhttp:3.4.1. For me, this is like the "Hello World" of OkHttp in Android Studio.
In MainActivity.java:
public void postSomething(View view) {
String url = "https://example.com/json_api_test.php";
String json = "{\"name\":\"cholula\"}";
OkHttpPostHandler handler = new OkHttpPostHandler();
String result = "";
try {
result = handler.execute(url, json).get();
} catch (Exception e) {
e.printStackTrace();
}
displayPostResponse(result + "\n");
}
My OkHttpPostHandler.java:
public class OkHttpPostHandler extends AsyncTask<String, Void, String> {
OkHttpClient client = new OkHttpClient();
public static final MediaType JSON
= MediaType.parse("application/json; charset=utf-8");
#Override
protected String doInBackground(String... params) {
RequestBody body = RequestBody.create(JSON, params[1]);
Request request = new Request.Builder()
.url(params[0])
.post(body)
.build();
try {
Response response = client.newCall(request).execute();
return response.body().string();
}
catch( Exception e ){
return "HTTP Request Error";
}
}
}
When I debug, I can see that the params[1] value is the expected json-like string, but that's the last time I see it.
I've tried forming the json in a number of ways, so I'm not sure if that's the problem. I just don't understand why I can't see the posted vars when the post request gets to the server.
How can I see the posted vars on the webserver? What am I doing wrong? I've only been using Android Studio and Java for less than a week, so I have no clue. I've really looked around the internet a lot for the answer, and so posting here is the last resort.
Thanks!
It turns out that one must use php://input to get the posted data.

Restlet client use authentication key

Using restlet JEE 2.3.2.
I have a client id and secret to interact with the server restful API. Submitting that info gets me back an authorization key that must be used for subsequent request. In curl, I can make queries using that key and can get data back:
curl -XGET "Authorization c79cec57-a52f-4e04-f3ca-55ea2a202114" "https://some/restful/endpoint"
How do I set my client resource to submit that authorization key? The online docs doesn't seem to cover this scenario.
if the scheme is not important, you can use a "Custom" scheme, (as it is mandatory in HTTP specification"). In order to avoid the warning "scheme is not supported by restlet engine", just register one, as follow:
You can achieve what you need using a "custom" scheme, as follow.
// Declare a custom Authenticator helper, if it is not standard
Engine.getInstance().getRegisteredAuthenticators().add(new AuthenticatorHelper(ChallengeScheme.CUSTOM, true, false) {});
// set up the reusable challenge response
ChallengeResponse cred = new ChallengeResponse(ChallengeScheme.CUSTOM);
cred.setRawValue("12344");
ClientResource cr = new ClientResource("http://localhost:8183/");
cr.setChallengeResponse(cred);
cr.get();
If you want an empty scheme, you can do as follow:
ChallengeResponse cred = new ChallengeResponse(new ChallengeScheme("",""));
cred.setRawValue("12345");
In this case, I think that you can use challenge response as described since such feature builds the Authorization header using format Authorization: Scheme ChallengeResponseContent:
ClientResource resource = new ClientResource(resouceURL);
String token = "myToken";
ChallengeResponse cr = new ChallengeResponse(
ChallengeScheme.HTTP_OAUTH_BEARER);
cr.setRawValue(token);
resource.setChallengeResponse(cr);
(...)
As a matter of fact, Restlet requires a challenge scheme that will be added before the token (or something else) within the value of the header Authorization. See extract from class AuthenticatorUtils#formatRequest:
public static String formatRequest(ChallengeRequest challenge,
Response response, Series<Header> httpHeaders) {
String result = null;
if (challenge == null) {
Context.getCurrentLogger().warning(
"No challenge response to format.");
} else if (challenge.getScheme() == null) {
Context.getCurrentLogger().warning(
"A challenge response must have a scheme defined.");
} else if (challenge.getScheme().getTechnicalName() == null) {
Context.getCurrentLogger().warning(
"A challenge scheme must have a technical name defined.");
} else {
ChallengeWriter cw = new ChallengeWriter();
cw.append(challenge.getScheme().getTechnicalName()).appendSpace();
int cwInitialLength = cw.getBuffer().length();
if (challenge.getRawValue() != null) {
cw.append(challenge.getRawValue());
} else {
(...)
In your case, I think that you need to build the header Authorization by yourself as described below:
ClientResource resource = new ClientResource(resouceURL);
String token = "myToken";
resource.getRequest().getHeaders().add("Authorization", token);
resource.get();
You can also implement a custom client resource for your needs in order to automatically apply the token:
public class ProtectedClientResource extends ClientResource {
private String token;
public ProtectedClientResource(String uri) {
super(uri);
}
#Override
public Response handleOutbound(Request request) {
if (token!=null) {
request.getHeaders().add("Authorization", token);
}
return super.handleOutbound(request);
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
Hope it helps you,
Thierry

Want the navigating page on the same page

my ajax some how looks like this :
function getXMLHttpRequest() {
var xmlHttpReq = false;
if (window.XMLHttpRequest) {
xmlHttpReq = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
xmlHttpReq = new ActiveXObject("Msxml2.XMLHTTP");
} catch (exp1) {
try {
xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
} catch (exp2) {
xmlHttpReq = false;
}
}
}
return xmlHttpReq;
}
function makeRequest() {
var xmlHttpRequest = getXMLHttpRequest();
xmlHttpRequest.onreadystatechange = getReadyStateHandler(xmlHttpRequest);
xmlHttpRequest.open("POST", "http://abc.com:8080/someservletServlet/", true);
xmlHttpRequest.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
xmlHttpRequest.send(null);
}
function getReadyStateHandler(xmlHttpRequest) {
return function() {
if (xmlHttpRequest.readyState == 4) {
if (xmlHttpRequest.status == 200) {
document.getElementById("xml").value = xmlHttpRequest.responseText;
} else {
alert("HTTP error " + xmlHttpRequest.status + ": " + xmlHttpRequest.statusText);
}
}
};
} but somehow the servlet is not bringing the response it should bring. can you help. what could be the possible error.
Ajax is the way to go.Because if you submit the request, page will refresh whether its same page or different.
If you still want to achieve it using without using ajax and refresh of page is fine with you then see if you have this kind of code in your servlet which is causing to forward it to some other page
String nextJSP = "nextPage.jsp";
RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(nextJSP);
dispatcher.forward(request,response);
If you need to load some data from some other URL you'll need to send an AJAX request (explain where to get data from) and handle the AJAX response (explain what to do with the fetched data). To provide for a browser-compatible solution you'd better use some well-known JS library. For example, you could use jQuery in which case your script could look like:
$.ajax({
url: "servletURL",//servlet URL to post data to
type: "POST",//request type, can be GET
cache: false,//do not cache returned data
data: {id : idOfData},//data to be sent to the server
dataType: "xml"//type of data returned
}).done(function(data) {
//do something with XML data returned from server
});
With this approach you need to call the above JS code, probably wrapped in a JS function, on some JS event, i.e. click, and handle response data, for example, by appending its contents to your text area.

Categories