Android InputStream following a rewriterule - android

I'm trying to parse an xml file from a website. Let's say the website is "http://example.com"
This website has a htaccess rewrite rule setup to redirect anything with a "www" prefix to the host back to example.com. so "http://www.example.com" would redirect to "http://example.com"
In my code I have a URL that i get the InputStream of.
protected InputStream getInputStream() {
try {
return feedUrl.openConnection().getInputStream();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
In this case feedUrl is poingting to "http://www.example.com/file.xml" and when I do the following:
try {
Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
} catch (Exception e) {
throw new RuntimeException(e);
}
I get an exception thrown and I believe it's not redirecting to "http://example.com/file.xml"
I could obviously just statically change where my feedUrl variable is pointing to, but I need this to be dynamic.

If anyone ran into this problem like I did, then here's the solution. The HttpURLConnection is already setup to follow redirects by default if the response code is 300, 301, 302, or 303.
For some reason, the server I'm parsing from needs to have the response code be 307 which Android does not redirect automatically.
I would suggest using a different response code, but if your server needs it then here's work around.
HttpURLConnection conn = (HttpURLConnection) feedUrl.openConnection();
int responseCode = conn.getResponseCode();
if( responseCode == 307 ){
String location = conn.getHeaderField("location");
feedUrl = new URL(location);
conn = (HttpURLConnection) this.feedUrl.openConnection();
}
Now conn can open an input stream to the correct file.

Related

HttpURlConnection not connecting

I am making a HttpUrlConnection with an Usgs API. This is my Url:
"https://earthquake.usgs.gov/fdsnws/event/1/queryformat=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10"
After thoroughly debugging, it seems that after connection.connect connection fails and jsonResponse is empty.
public static String makeHttprequest(URL url) throws IOException {
String jsonResponse = "";
HttpURLConnection connection = null;
InputStream stream = null;
try {
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setReadTimeout(1000000);
connection.setConnectTimeout(1500000);
connection.connect();
stream = connection.getInputStream();
jsonResponse = readfromstream(stream);
} catch (IOException e) {
Log.e("IOException", "Error while making request");
}
return jsonResponse;
}
This is Log
Everything looks good. It seems to me that you have no internet connection in your running devices. Probably you are using emulator in your computer which is not connected to internet.
Please try to run in real device. It is working perfect for me.
A bit advice, please try to use libraries such as Retrofit or OkHttp. They are very much easier and handier than these old ways.
If you insist using HttpURLConnection, try the following
URL url = new URL(yourUrlString);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
} finally {
urlConnection.disconnect();
}
Or for more formal use of HttpURLConnection, visit here. It shows several proper use of HttpURLConnection APIs.
https://developer.android.com/reference/java/net/HttpURLConnection
just tried my app on real device everything is working as expected there might be problem with emulator.

Android: HttpUrlConnection object returns error 301 after connection

I'm trying to connect to a web API using a URL. However, I get a 301 error from the server (Moved Permanently), although the provided URL works very well with no errors when I try it in my browser.
Here is the code that builds the URL:
public Loader<List<Earthquake>> onCreateLoader(int i, Bundle bundle) {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
String minMagnitude = sharedPrefs.getString(
getString(R.string.settings_min_magnitude_key),
getString(R.string.settings_min_magnitude_default));
String orderBy = sharedPrefs.getString(
getString(R.string.settings_order_by_key),
getString(R.string.settings_order_by_default)
);
Uri baseUri = Uri.parse(USGS_REQUEST_URL);
Uri.Builder uriBuilder = baseUri.buildUpon();
uriBuilder.appendQueryParameter("format", "geojson");
uriBuilder.appendQueryParameter("limit", "10");
uriBuilder.appendQueryParameter("minmag", minMagnitude);
uriBuilder.appendQueryParameter("orderby", orderBy);
Log.i ("the uri is ", uriBuilder.toString());
return new EarthquakeLoader(this, uriBuilder.toString());
}
Here is the code that tries to connect to the resource represented by the URL:
private static String makeHttpRequest(URL url) throws IOException {
String jsonResponse = "";
// If the URL is null, then return early.
if (url == null) {
return jsonResponse;
}
Log.i("The received url is " , url +"");
HttpURLConnection urlConnection = null;
InputStream inputStream = null;
try {
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setReadTimeout(10000 /* milliseconds */);
urlConnection.setConnectTimeout(15000 /* milliseconds */);
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// If the request was successful (response code 200),
// then read the input stream and parse the response.
if (urlConnection.getResponseCode() == 200) {
inputStream = urlConnection.getInputStream();
jsonResponse = readFromStream(inputStream);
} else {
Log.e(LOG_TAG, "Error response code: " + urlConnection.getResponseCode()); //this log returns 301
}
} catch (IOException e) {
Log.e(LOG_TAG, "Problem retrieving the earthquake JSON results.", e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (inputStream != null) {
// Closing the input stream could throw an IOException, which is why
// the makeHttpRequest(URL url) method signature specifies than an IOException
// could be thrown.
inputStream.close();
}
}
return jsonResponse;
}
I could know that the connection returns status code of 301 from the log provided in the case when the status code is not 200. I have also logged the generated URL, I copied it from the logcat and tried it in my browser and it worked well. Here is the built URL: http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&limit=10&minmag=6&orderby=magnitude
I checked this question: Android HttpURLConnection receives HTTP 301 response code but it wasn't clear to me what is the solution for the problem.
Can you please help me identify and solve the problem?
UPDATE: As greenapps indicated in his comment, the connection is done through https. That comment identified the problem and helped me fix the code.
In my code, the string I used to build the basic URL, had the protocol value as http not https, it was:
private static final String USGS_REQUEST_URL =
"http://earthquake.usgs.gov/fdsnws/event/1/query";
After reading greenapps comment, I just changed the protocol part in the string to https, so it became:
private static final String USGS_REQUEST_URL =
"https://earthquake.usgs.gov/fdsnws/event/1/query";
That solved the problem.
Thanks.
If you click your http link here you will see that the browser shows a https page. You better use that url directly as there is redirection now.
This is because the address http to https transferred.
To avoid this, you need to convert the request address to https.

Android: how can I make an HTTP HEAD request?

I would like to make a simple HTTP HEAD request, without keep-alive.
How can I do that in Android?
using HttpClient:
As njzk2 suggested, with HttpClient() it's very straightforward:
HttpResponse response = new HttpClient().execute(new HttpHead(myUrl));
However there is a problem with not being able to close the connection. Usually on the HttpClient, you would get the entity using:
HttpEntity entity = response.getEntity();
and then you would get the input stream from the entity
InputStream instream = entity.getContent();
...
instream.close();
and by closing the input stream, the connection would close.
However, in the case of a HEAD request, the entity appears to be null (possibly because HEAD requests don't return the body in the response), so the input stream cannot be fetched and closed and the connection doesn't close either.
In the last edit to his answer, njzk2 is suggesting to use AndroidHttpClient, which is a more recent implementation (API 8) of HttpClient and it actually has a close() method. I haven't used it but I guess it will work fine. However, as the Android development team suggests, the HttpUrlConnection should be the preferred Android client to use.
using HttpUrlConnection:
Actually it seems quite easy to make HEAD requests using HttpUrlConnection and make sure that the connection closes:
HttpURLConnection urlConnection = null;
System.setProperty("http.keepAlive", "false");
try {
URL url = new URL(stringUrl);
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("HEAD");
urlConnection.getInputStream().close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
Trivially :
HttpResponse response = new AndroidHttpClient().execute(new HttpHead(myUrl));
Typically you'll use the same AndroidHttpClient for several connections, then call close on it.
For ordinary Java and Android
I am using some standard Java code to test the existence of a resource and in the same time to check whether a resource has been changed, provided the parameter if_modified_since is non-zero.
URL url = new URL(adr);
try {
URLConnection con = url.openConnection();
con.setIfModifiedSince(if_modified_since);
if (con instanceof HttpURLConnection) {
/* Workaround for https://code.google.com/p/android/issues/detail?id=61013 */
con.addRequestProperty("Accept-Encoding", "identity");
((HttpURLConnection) con).setRequestMethod("HEAD");
int response = ((HttpURLConnection) con).getResponseCode();
if (response == HttpURLConnection.HTTP_UNAVAILABLE)
return false;
if (response == HttpURLConnection.HTTP_NOT_MODIFIED)
return false;
}
if (if_modified_since != 0) {
long modified = OpenOpts.getLastModified(con);
if (modified != 0 && if_modified_since >= modified)
return false;
}
InputStream in = con.getInputStream();
in.close();
return true;
} catch (FileNotFoundException x) {
return false;
} catch (UnknownHostException x) {
return false;
} catch (SocketException x) {
return false;
}
Interestingly the code needs a con.getInputStream() and I don't get some errors here. But I needed some helper code, to also cater for URIs that point to JARs. The helper code is:
private static long getLastModified(URLConnection con)
throws IOException {
if (con instanceof JarURLConnection) {
return ((JarURLConnection) con).getJarEntry().getTime();
} else {
return con.getLastModified();
}
}
The code can be further optimized by some specialization if the
URI is schema file: , one can then directly do File.exists() and File.getLastModified().
We do not throw a ServiceUnvailable exception here, we basically assume that the outer code would catch an IOException and then assume a false
result of the getHead().

HttpURLConnection returns 404 when called from an IntentService

I am attempting to build an IntentService that uploads files using a REST Api.
I am able to start the IntentService from my main Activity, and the IntentService can pass a message back to the Activity so I know the IntentService is working.
My issue is when I run this code:
#Override
protected void onHandleIntent(Intent intent) {
// do the uploading stuff
try {
URL url = new URL(this.url + fileName);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("PUT");
conn.setDoOutput(true);
BufferedInputStream buffInputStream = new BufferedInputStream(new FileInputStream(filePath));
BufferedOutputStream buffOutputStream = new BufferedOutputStream(conn.getOutputStream());
HttpUtils.copyBufferStream(buffInputStream, buffOutputStream);
if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
BufferedInputStream responseBufferedInput = new BufferedInputStream(conn.getInputStream());
byte[] body = HttpUtils.readStream(responseBufferedInput);
result = HttpUtils.convertBytesToString(body);
Log.e("Result:", result);
}else{
Log.e("conn.getResponseCode()", Integer.toString(conn.getResponseCode()));
}
} catch (IOException e) {
e.printStackTrace();
}
}
in the IntentService onHandleIntent method, I always get a 404 (or FileNotFoundException when not checking conn.getResponseCode()).
Calling the exact same code in my main Activity, uploads the file and completes successfully.
I have no idea why this is happening? Any ideas?
Try printing the URL you're trying to open, you will probably find out that there's something wrong with it...maybe it's just a missing slash ;-)
404 is the HTTP error for not found..meaning that the URL is invalid on that server.

Android: HttpURLConnection redirect - doInBackground

I'm using HttpURLConnection to do communication with a backend server and im doing so in an async task in the doInBackground method as you should.
Now I need to be able to follow 302 redirects, but I'm having some problems with this. The issue is that the new location usually will be on another host, however when doing the redirect request it seem not to change the URL to a new host hence I get a 404 error saying the specified path does not exits.
Now I know I could set HtppURLConnection.setFollowRedirect but I need to have more control over the redirects so they should not just be followed blindly. The Redirect behavour should be controlled by the object who called the asynctask (when an asynctask object is created you pass the object who creates it in a parameter called _callback).
Heres's my current code:
protected HttpResponse doInBackground(String... req) {
HttpURLConnection urlConnection = null;
try {
urlConnection = (HttpURLConnection) this._url.openConnection();
urlConnection.setConnectTimeout( (int) this._timeout*1000);
String body = req[0];
// set headers / write information to output stream if request is post
// create the response object
HttpResponse responseObject = null;
try
{
// get status, contenttype, charset...
InputStream in = null;
if (urlConnection.getResponseCode() != -1 && urlConnection.getResponseCode() < 300)
{
in = new BufferedInputStream(urlConnection.getInputStream(), 8192);
}
else
{
in = new BufferedInputStream(urlConnection.getErrorStream(), 8192);
}
responseObject = new HttpResponse(in, status, contentType, charset);
// if redirect
if (status == 302 && this._callback.onRedirect(responseObject) == true)
{
// recall
String url = urlConnection.getHeaderField("location");
Log.v("Async Task", "Redirect location: " + url);
this._url = null;
this._url = new URL(url);
urlConnection.disconnect();
urlConnection = null;
responseObject = this.doInBackground(req);
}
} catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// return the response
return responseObject;
}
// catch some other exceptions
finally
{
if (urlConnection != null)
{
urlConnection.disconnect();
} }
}
And as said the problem is that the redirect request seem to change the path of the URL but not the host. The URL object itself seem to contain the right information so I have no idea why this is happening. (I'm getting HTML as response which is an 404 error page that includes the server name of the old server)
Thanks for any help!
Note: HttpResponse is just an object I created for holding the relevant information about the response.
This was caused by the fact that I sent the same headers and did not change the "host" header of the request which caused Apache to be confused it seems.

Categories

Resources