I am using a WebView in my app in which I must intercept requests. I am currently using the follwing code to do it.
public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestProperty("User-Agent", userAgent);
String mime;
if (url.lastIndexOf('.') > url.lastIndexOf('/')) {
String ext = url.substring(url.lastIndexOf('.') + 1);
mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
} else {
mime = "text/html";
}
return new WebResourceResponse(mime, "UTF-8", conn.getInputStream());
}
Above code works fine in most cases, but no all. For example when I try to login to Outlook, it just shows that my email or password is incorrect, I have also seen other cases in which requests get broken, but everything works fine if I remove shouldInterceptRequest.
Is there any better way that the one I am currently using to intercept requests?
There are two issues with you code
Incorrect extension detection
For example, when the code try to get resource extension for this URL:
https://login.live.com/login.srf?wa=wsignin1.0&rpsnv=12&ct=1442476202&rver=6.4.6456.0&wp=MBI_SSL_SHARED&wreply=https:%2F%2Fmail.live.com%2Fdefault.aspx%3Frru%3Dinbox&lc=1033&id=64855&mkt=en-us&cbcxt=mai
It will return aspx%3Frru%3Dinbox&lc=1033&id=64855&mkt=en-us&cbcxt=mai which is wrong. There is special method for getting extension from the URL: getFileExtensionFromUrl()
According to documentation method MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) may return null. In this case your code set wrong mime type for the page.
Here is the method code that take into account both these issues
#Override
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
String ext = MimeTypeMap.getFileExtensionFromUrl(url);
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
if (mime == null) {
return super.shouldInterceptRequest(view, url);
} else {
HttpURLConnection conn = (HttpURLConnection) new URL(
url).openConnection();
conn.setRequestProperty("User-Agent", userAgent);
return new WebResourceResponse(mime, "UTF-8",
conn.getInputStream());
}
}
Related
when using androids URL and HttpUrlConnection to send a GET request to a backend point, it sometimes (1 out of 10) occurs, that the request fails due to:
java.net.ProtocolException: Unexpected status line: 1.1 200 OK
As said this only happens somtimes, I tried 3 different backends (one of them self hosted) and it still occurs.
System.setProperty("http.keepAlive", "false");
URL url = new URL(callUrl);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setUseCaches(false);
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
con.setConnectTimeout(5000);
con.setReadTimeout(4000);
con.setRequestMethod(requestMethod);
con.setRequestProperty("User-Agent", USER_AGENT);
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setRequestProperty("charset", "UTF-8");
con.setRequestProperty("Connection", "close");
Maybe someone has an idea how to fix it?
First of all, this is the API references link:
http://square.github.io/retrofit/
So, go to:
File > Project Structure , after opened, in modules - app, go to tab Dependencies.
Click on the + symbol and add library
Search and add this library: com.squareup.retrofit2:retrofit:2.30, and I highly recommend you to use Jackson too, if you want it, add this library to: com.squareup.retrofit2:converter-jackson:2.3.0
After all depedencies and building, let's go to the code.
I created a RetrofitInitialization class with this code:
public class RetrofitInicializador {
public RetrofitInitialization() {
String url = "localhost:8080/webservice/";
retrofit = new Retrofit.Builder().baseUrl(url)
.addConverterFactory(JacksonConverterFactory.create()).build();
}
}
We need to create a service too, so, I created a service class called:
ObjectService
public interface ObjectService {
#POST("post/example")
Call<Object > postexemple(#Body Object object);
#GET("get/example/{id}")
Call<Object> getexemple(#Path("id") Integer id);
}
The Object is your model that you want to receive or send.
After this, add your service into the RetrofitInitialization, after the constructor.
Similar this:
public ObjectService getObjectService() {
return retrofit.create(ObjectService.class);
}
In your Activity or anywhere you want to get this informations, do something like this:
private void loadFromWS(Object object) {
Call<Object> call = new RetrofitInicializador().getObjectService().postexemple(object);
call.enqueue(new Callback<Object>() {
#Override
public void onResponse(Call<Object> call, Response<Object> response) {
Object response = response.body();
// DO your stuffs
}
#Override
public void onFailure(Call<Object> call, Throwable t) {
Toast.makeText(AgendaActivity.this, "Connection error", Toast.LENGTH_SHORT).show();
}
});
}
Edit: Forget to tell, I used this on WS REST server (to be more specific: JAX-RS and Jersey WS)
Android 21+
We have a WebViewClient which overrides shouldInterceptRequest method. In this method, we make an HTTP GET request manually and pass received InputStream as a WebResourceResponse, fully expecting WebView to display received data. Received content is gzipped HTML.
#Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
HttpURLConnection urlc = null;
try {
URL url = new URL(request.getUrl().toString());
urlc = (HttpURLConnection) network.openConnection(url); // network instance is received via some other way, unrelated to this problem
urlc.setRequestMethod("HEAD");
urlc.connect();
String contentType = urlc.getContentType();
String contentEncoding = urlc.getContentEncoding();
urlc.disconnect();
urlc = (HttpURLConnection) network.openConnection(url);
urlc.connect();
return new WebResourceResponse(contentType, contentEncoding, urlc.getInputStream());
} catch (Exception e) {
...
}
However, WebView displays only text - it doesn't display proper HTML, it only displays raw content, this is an example of what is shown on the page:
<html>
<head>SomeTitle</head>
<body>content....</body>
</html>
Looks like it either doesn't understand that provided data is HTML, or fails to parse it (maybe because of gzip?). Or is something else going on?
Content-type and content-encoding received with HEAD call are:
HEAD REQUEST | CONTENT TYPE: text/html;charset=UTF-8 | ENCODING: gzip
and are passed to WebResourceResponse, as shown in the code above.
Any ideas..?
It seems to be necessary to remove the semi-colon and everything after it from the content type string.
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
}
I have a weird question. I am working on a Java project for work, where we need to make HTTP GET/POST calls to our WEB API. I wanted to make a WebAPI testing project in C#; run it locally (localhost on some random port) and make sure I am sending the right stuff. That way I could control what was sent back(success, errors, JSON, XML, and different variables like that).
Here is some key stuff I have so far:
Client-Java code:
public String sendAPIRequest( HttpRequestMethod method, String apiURI, String payload) throws IOException
{
// Method is GET, POST....
// apiURL specific API navigating to.
// pauload is the html body.
if(payload == null)
{
payload = "";
}
// Establish a connection.
String strURL = String.format("%s%s", this.BaseURL, apiURI);
URL url = new URL(strURL);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestProperty("Accept-Charset", this.CHARSET);
conn.setRequestMethod(method.toString());
conn.setRequestProperty("Content-Type", "text/json;charset=" + this.CHARSET);
conn.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
conn.setRequestProperty("Accept","*/*");
conn.setDoInput(true);
conn.setDoOutput(true);
conn.connect();
// write the payload out, if it exists.
//if(payload != null)
{
try(OutputStream output = conn.getOutputStream())
{
output.write(payload.getBytes(CHARSET));
}
}
// read the response.
StringBuilder response = new StringBuilder();
InputStream input = conn.getInputStream();
try(Scanner inputScanner = new Scanner(input))
{
while(inputScanner.hasNextLine())
{
response.append((inputScanner));
}
}
return response.toString();
}
public String CheckForApplicableLicenses(String dCode, String key)
{
String result;
try
{
String APICall = String.format("/license/find_matching?d_code=%s&key=%s", dCode, key);
String Response = API.sendAPIRequest(HttpRequestMethod.GET, APICall);
// TODO Parse the String Response JSON/XMl.
result = Response;
}
catch(Exception ex)
{
// TODO: incorporate some sort of logging and error handling.
result = ex.toString();
}
return result;
}
Server-C#.Net code (tested with fiddler, and in the browser):
[Route("api/[controller]")]
public class LicenseController : Controller
{
[HttpGet]
[Route("find_matching")]
public IEnumerable<string> find_matching(string d_code = "", string key = "")
{
return new string[] { d_code, key };
}
}
Results so far:
I've gotten 404 errors, and I have been able to connect. Most of the time the Java client blows up when I get to the creating the InputStream. I've never been able to trip the breakpoint in the C# server.
Questions:
1) Is what I am doing even feasible? I'm really just trying to test the Java Client, without calling the API, before I am ready. Maybe it has something to do with not running the service on the default HTTP port of 80?
2) Is there a better way of testing this? I don't want to make call to our actual service until we are done.
Thanks in advance for an assistance.
I am trying to get some acquaintance in using facebook API using Java (restfb). I am trying out the following code.
public class FBJava {
private String API_Key = "xxxxxx";
private String API_Secret = "xxxxxxxx";
public String firstReq = "https://graph.facebook.com/oauth/authorize?client_id="+API_Key+"&" +
"redirect_uri=http://www.facebook.com/connect/login_success.html& scope=publish_stream,offline_access,create_event";
public String secondReq = "https://graph.facebook.com/oauth/access_token?client_id="+API_Key+"" +
"&redirect_uri=http://www.facebook.com/connect/login_success.html&client_secret="+API_Secret+"&code=";
public static void main(String[] args) throws IOException {
FBJava fb = new FBJava();
System.out.println(fb.firstReq);
URL request = new URL(fb.firstReq);
HttpURLConnection conn = (HttpURLConnection) request.openConnection();
conn.connect();
int code = conn.getResponseCode();
System.out.println(code);
}
}
When I run the firstReq string in the browser manually, it is redirecting me to the correct page. But when I check the response code I am getting a 400 which means that its a bad request. I want to know why does it respond differently when I try to run it through the program. I know I am doing something wrong, but want to know what is the mistake and why is it occurring? Any kind of insight in this matter would be appreciated.
There is an error in the firstReq url. It contains a whitespace character between "& scope". Try this (I just removed the whitespace):
public String firstReq = "https://graph.facebook.com/oauth/authorize?client_id="+API_Key+"&" +
"redirect_uri=http://www.facebook.com/connect/login_success.html&scope=publish_stream,offline_access,create_event";
I am currently trying to debug an Android App built around WebView. The development network environment that I am tasked to deal with (not my choice, it is an 'enterprisey' security decision) is WPA WiFi + proxy server + proxy authentication.
While the instructions on a very helpful previous answer were great, I'm trying to find a way to configure both proxy host:port and username:password.
My constraints are:
Phone is not rooted - trying to reproduce a customer-reported bug, would rather not deviate from typical customer setup
Running a Samsung Galaxy S on Froyo
Built against 2.1
Android apps aren't my usual thing, again not my choice, so if I'm blatantly missing details, be nice ;)
With WebView android proxy configuration, for basic scheme preemptive proxy authentication,
Starting from Android 2.2, the extra header can be set for authentication. The following can add a header for webView's http request:
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
UsernamePasswordCredentials creds= new UsernamePasswordCredentials(proxyUserName, proxyPassword);
Header credHeader = BasicScheme.authenticate(creds, "UTF-8", true);
Map<String, String> header = new HashMap<String, String>();
header.put(credHeader.getName(), credHeader.getValue());
view.loadUrl(url, header);
}
For older version, the preemptive proxy authentication can be set on mProxyUserName and mProxyPassword in android.webkit.Network by reflection:
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
try{
Class networkClass = Class.forName("android.webkit.Network");
if (networkClass != null) {
Object networkObj = invokeMethod(networkClass, "getInstance", new Object[]{view.getContext()}, Context.class);
if (networkObj != null) {
Field mProxyUserName = obj.getClass().getDeclaredField("mProxyUserName");
mProxyUserName.setAccessible(true);mProxyUserName.set(networkObj, proxyUserName);
Field mProxyPassword = obj.getClass().getDeclaredField("mProxyPassword");
mProxyPassword.setAccessible(true);mProxyPassword.set(networkObj, proxyPassword);
}
}
}catch(Exception e){
e.printStackTrace();
}
view.loadUrl(url);
}
When you load a new url, both loadUrl() must need to call again. That is very important.
Therefore, a custom WebViewClient should be used to override shouldOverrideUrlLoading(WebView view, String url)
class ProxyAuthWebViewClient extends WebViewClient {
String proxyUserName;
String proxyPassword;
public ProxyAuthWebViewClient(String proxyUserName, String proxyPassword){
this.proxyUserName = proxyUserName;
this.proxyPassword = proxyPassword;
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
loadUrl(view, url, proxyUserName, proxyPassword);
return true ;
}
}
And set the WebViewClient on your webView:
webView.setWebViewClient(new ProxyAuthWebViewClient("user", "password"));