I have an android app which works perfectly on my phone (Huawei Mate 10 Pro) but when I try to run it on my tablet (Samsung Galaxy Tab S2) I get a java.io.FileNotFoundException when the app tries to access the input stream.
(I get the exception at "InputStream stream = connection.getInputStream();")
Is there a difference between Samsung and other devices regarding http communication? how can I write the code to work on every device?
Here's the code:
public String download_organisations(String url){
String jsonString = "";
try {
if(!url.startsWith("http://")){
url = "http://" + url;
}
if(url.endsWith("/")){
url = url.substring(0, url.lastIndexOf("/"));
}
url = url + ManagerSystemStatic.URL_SERVICE_WEB_ORGANISATION_MANAGER;
URL httpUrl = new URL(url);
HttpURLConnection connection = (HttpURLConnection) httpUrl.openConnection();
connection.setConnectTimeout(10000);
InputStream stream = connection.getInputStream();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = stream.read(data, 0, data.length)) != -1) {
buffer.write(data, 0, nRead);
}
buffer.flush();
byte[] inputStreamByteArray = buffer.toByteArray();
byte[] base64 = Base64.decode(inputStreamByteArray, 0);
byte[] decrypted = CipherUtils.decrypt(base64);
jsonString = new String(decrypted);
stream.close();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
Log.d("OrganisationManager", "Download succeded with response: " + connection.getResponseCode());
} else {
Log.d("OrganisationManager", "Download failed with response: " + connection.getResponseCode());
}
} catch (MalformedURLException e) {
Log.e("OrganisationManager", e.toString());
return DOWNLOAD_FAILED;
} catch (IOException e) {
Log.e("OrganisationManager", e.toString());
e.printStackTrace();
return DOWNLOAD_FAILED;
} catch (Exception e) {
Log.e("OrganisationManager", e.toString());
return DOWNLOAD_FAILED;
}
return jsonString;
}
Here's the StackTrace (with my url replaced):
E/OrganisationManager: java.io.FileNotFoundException: http://www.myurlhere.com
W/System.err: java.io.FileNotFoundException: http://www.myurlhere.com
W/System.err: at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:254)
W/System.err: at com.organisationmanager.ble.common.WebPortalCommunicationHelper.download_organisations(WebPortalCommunicationHelper.java:210)
at com.organisationmanager.ble.ScanningActivity$UpdateOrganisations.doInBackground(ScanningActivity.java:414)
at com.organisationmanager.ble.ScanningActivity$UpdateOrganisations.doInBackground(ScanningActivity.java:403)
W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
D/InputTransport: Input channel constructed: fd=82
W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
W/System.err: at java.lang.Thread.run(Thread.java:762)
When it starts the http communication I also get this in the debugger console:
D/NetworkSecurityConfig: No Network Security Config specified, using platform default
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
Is that relevant?
After a lot of trial and error I still don't know what the issue was, but I tried using the class OkHttp instead of HttpUrlConnection, and suddenly everything worked. I have no idea why, but since I now have a working code I no longer need help with this issue. I hope this solution (changing class entirely) may help someone in the future. More about the class I used can be found here: http://square.github.io/okhttp/
Related
I'm having trouble letting android devices with an API < 21 connect to my nginx server. All results here are from an API 19 emulated device.
I tried using the VolleyToolboxExtension and NoSSLv3Factory as suggested elsewhere but still got hard errors like below, except then instead of sslv3 it was tlsv1.
My current approach is to let old android versions connect via SSLv3.But this still produces errors like down below.
Note: I'm aware that SSLv3 is broken but I'm using a different certificate and I'm not sending anything security relevant. The whole idea is to make a survey so I can decide whether I want to drop support for older android SDK versions.
error during request: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0xb8a45890: Failure in SSL library, usually a protocol error
error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:741 0x8d959990:0x00000000)
I'm using the following code for connecting:
final String url;
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ){
url = "https://abc.badssl.example.com";
} else {
url = "https://abc.example.com";
}
final JSONObject jsonBody;
try {
jsonBody = new JSONObject("{\"api\":" + Build.VERSION.SDK_INT + "}");
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, url,
jsonBody,
response -> {
try {
Log.d(TAG, "Response is: " + response.getInt("api"));
} catch (JSONException e) {
Log.wtf(TAG, e);
}
wasRunning = true;
loading.postValue(false);
}, e -> {
Log.e(TAG, "error during request: " + e.getMessage());
error.postValue(true);
});
jsonObjectRequest.setShouldCache(false);
jsonObjectRequest.setRetryPolicy(new DefaultRetryPolicy(DefaultRetryPolicy.DEFAULT_TIMEOUT_MS, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
queue.getCache().clear();
queue.add(jsonObjectRequest);
} catch (JSONException e) {
Log.wtf(TAG, e);
}
And this is my nginx configuration for abc.badssl.example.com, using a different certificate:
listen 443 ssl;
server_name abc.badssl.example.com;
ssl on;
ssl_protocols SSLv3;
ssl_dhparam /etc/nginx/ssl/dhparams.pem;
#worker shared ssl cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 5m;
ssl_ciphers 'ECDHE-ECDSA-AES256-SHA:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:!DHE-RSA-AES256-GCM-SHA384:!DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES256-SHA:!DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4:!DHE-RSA-DES-CBC-SHA:!DHE-RSA-CAMELLIA256-SHA:!DHE-RSA-CAMELLIA128-SHA:!DHE-DSS-CBC-SHA:!DHE-RSA-DES-CBC3-SHA';
fastcgi_param HTTPS on;
I think I got the nginx params wrong but I can't seem to find much as 99% is either about disabling SSLv3 or nothing regarding the current version of volley (dead links in SO).
Ok so the solution turns out to be a bit different.
First of all: SSLv3 is disabled on debian & ubuntu in current versions of OpenSSL for obvious reasons. Interestingly this does not throw any errors or such, the nginx configuraton protocols are simply ignored ?! I can still connect via TLS1.X to the server, even with only SSLv3 in the configuration. nmap reports the same.
On Android < 21 (as told in other Answers) TLS is existent, but SSLv3 preferred or even forced.
The easiest solution to get a working HTTPsUrlConnection on older devices is to use NetCipher.
build.gradle:
implementation "info.guardianproject.netcipher:netcipher:2.0.0-alpha1"
And then use Netcipher for creation of the HTTPs connection.
You can also use this together with volley, by using a custom HttpStack. Please note that StrongBuilders from Netcipher aren't for making simple https connections! They're only for making connections through the Tor network. So don't get fooled thinking you can just use these to avoid HttpStack.
The request handler without volley, using the raw HttpsUrlConnection, stripped down:
final String url = "https://asd.example.com";
runner = new Thread(() -> {
final JSONObject jsonBody;
try {
HttpsURLConnection con = NetCipher.getHttpsURLConnection(new URL(url));
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
con.setRequestProperty("Accept", "application/json");
con.setDoOutput(true);
con.setDoInput(true);
jsonBody = new JSONObject("{\"api\":" + Build.VERSION.SDK_INT + "}");
OutputStream wr = con.getOutputStream();
wr.write(jsonBody.toString().getBytes("UTF-8"));
wr.flush();
wr.close();
InputStream in = new BufferedInputStream(con.getInputStream());
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
result.write(buffer, 0, length);
}
JSONObject jsonObject = new JSONObject(result.toString("UTF-8"));
in.close();
con.disconnect();
Log.d(TAG,"received json: "+jsonObject.toString());
loading.postValue(false);
} catch (
Exception e) {
Log.wtf(TAG, e);
}
});
runner.start();
Symptoms:
Unable to complete getInputStream. It fails with code (HttpURLConnectionImpl.java:238)
Original error log:
05-25 17:57:06.473 2675-2722/com.manantial.raul.photogallery E/TAG: FlickrFetchr Failed to fetch items
java.io.FileNotFoundException: http://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=6f722a706254ed716d5abb9fb1f012c7&extras=url_s
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:238)
at com.manantial.raul.photogallery.FlickrFetchr.getUrlBytes(FlickrFetchr.java:37)
at com.manantial.raul.photogallery.FlickrFetchr.getUrl(FlickrFetchr.java:69)
at com.manantial.raul.photogallery.FlickrFetchr.fetchItems(FlickrFetchr.java:79)
at com.manantial.raul.photogallery.PhotoGalleryFragment$FetchItemsTask.doInBackground(PhotoGalleryFragment.java:45)
at com.manantial.raul.photogallery.PhotoGalleryFragment$FetchItemsTask.doInBackground(PhotoGalleryFragment.java:36)
at android.os.AsyncTask$2.call(AsyncTask.java:295)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
HttpURLConnection getResponseCode = 403 / HTPP_FORBIDDEN
When using the same URL via web browser, there is no error and getResponseCode is OK / 200.
Facts:
Code used:
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream(); // fails, while HttpURLConnection = HTTP_FORBIDDEN 403
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { // connection resp code 403 FORBIDDEN
return null;
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
String temp = out.toByteArray().toString();
Log.i("TAG", TAG + "out.toByteArray is the data response: " + temp);
return out.toByteArray();
} finally {
Log.i("TAG", TAG + " connection response code: " + connection.getResponseCode());
connection.disconnect();
}
The URL used in the code was:
http://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=6f722a706254ed716d5abb9fb1f012c7&extras=url_s
That was converted to https, when using it via browser, but not when using it via my test Android App. The solution was simply adjusting the URL to https in the code:
https://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=6f722a706254ed716d5abb9fb1f012c7&extras=url_s
I am trying to connect to a website to receive some JSON information. When I run the app in Android Studio using a connected Nexus 7 device I get a java.io.FileNotFound exception, but if I click on the name of the file that was not found, the response expected immediately shows in my browser. This is a new app for me, but I have done similar things in the past that have worked. I have been trying multiple things over the last 2 days and just can't seem to find the problem. Code blows up when I call connection.getInputStream(). All of this is running in an AsyncTask.
My Code
public byte[] getUrlBytes(String urlSpec) throws IOException{
URL url = new URL(urlSpec);
// urlSpec: https://api.weather.gov/points/48.0174,-115.2278
try {
connection = (HttpsURLConnection) url.openConnection();
} catch (IOException ioe) {
Log.d(TAG, "connection ioe: " + ioe.toString());
Toast.makeText(context, "#string/can_not_connect",
Toast.LENGTH_LONG).show();
}
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream in = connection.getInputStream(); *** Blows up here ****
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "connection Response code: " +
connection.getResponseMessage());
}
int bytesRead = 0;
byte[] buffer = new byte[1024];
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
out.close();
return out.toByteArray();
} finally {
connection.disconnect();
}
}
Logcat
04-02 15:40:59.693 32471-32495/com.drme.weathertest E/WeatherFetcher: Failed
to fetch items
java.io.FileNotFoundException: https://api.weather.gov/points/48.0174,-115.2278
*** Note that if I click on this file name, it works in my browser ***
at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:206)
at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getInputStream(DelegatingHttpsURLConnection.java:210)
at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:25)
at com.drme.weatherNoaa.WeatherFetcher.getUrlBytes(WeatherFetcher.java:148)
at com.drme.weatherNoaa.WeatherFetcher.getUrlString(WeatherFetcher.java:183)
at com.drme.weatherNoaa.WeatherFetcher.downloadGridPoints(WeatherFetcher.java:202)
at com.drme.weatherNoaa.WeatherFetcher.requestForecast(WeatherFetcher.java:262)
at com.drme.weatherNoaa.WeatherFragment$SearchTask.doInBackground(WeatherFragment.java:329)
at com.drme.weatherNoaa.WeatherFragment$SearchTask.doInBackground(WeatherFragment.java:296)
at android.os.AsyncTask$2.call(AsyncTask.java:292)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Thanks for the help.
After additional reading of webservice documentation (some of it seems a little thin to me) and some additional posts on the web, the answer is that they require Acceptance and User-agent headers in the request. I have made these changes and the code now works as desired.
New static constants
private static final String ACCEPT_PROPERTY = "application/geo+json;version=1";
private static final String USER_AGENT_PROPERTY = "xxxx.com (xxxxxxxxx#gmail.com)";
Code changes
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestProperty("Accept", ACCEPT_PROPERTY); // added
connection.setRequestProperty("User-Agent", USER_AGENT_PROPERTY); // added
No other changes were required, although it took me awhile to figure out how to add the headers and what their format might be.
Thanks for checking this out.
I'm writing a program that connects to a servlet thanks to a HttpURLConnection but I stuck while checking the url
public void connect (String method) throws Exception {
server = (HttpURLConnection) url.openConnection ();
server.setDoInput (true);
server.setDoOutput (true);
server.setUseCaches (false);
server.setRequestMethod (method);
server.setRequestProperty ("Content-Type", "application / xml");
server.connect ();
/*if (server.getResponseCode () == 200)
{
System.out.println ("Connection OK at the url:" + url);
System.out.println ("------------------------------------------- ------- ");
}
else
System.out.println ("Connection failed");
}*/
I got the error :
java.net.ProtocolException: Cannot write output after reading input.
if i check the url with the code in comments but it work perfectly without it
unfortunately, I need to check the url so i think the problem comes from the getResponseCode method but i don t know how to resolve it
Thank you very much
The HTTP protocol is based on a request-response pattern: you send your request first and the server responds. Once the server responded, you can't send any more content, it wouldn't make sense. (How could the server give you a response code before it knows what is it you're trying to send?)
So when you call server.getResponseCode(), you effectively tell the server that your request has finished and it can process it. If you want to send more data, you have to start a new request.
Looking at your code you want to check whether the connection itself was successful, but there's no need for that: if the connection isn't successful, an Exception is thrown by server.connect(). But the outcome of a connection attempt isn't the same as the HTTP response code, which always comes after the server processed all your input.
I think the exception is not due toprinting url. There should some piece of code which is trying to write to set the request body after the response is read.
This exception will occur if you are trying to get HttpURLConnection.getOutputStream() after obtaining HttpURLConnection.getInputStream()
Here is the implentation of sun.net.www.protocol.http.HttpURLConnection.getOutputStream:
public synchronized OutputStream getOutputStream() throws IOException {
try {
if (!doOutput) {
throw new ProtocolException("cannot write to a URLConnection"
+ " if doOutput=false - call setDoOutput(true)");
}
if (method.equals("GET")) {
method = "POST"; // Backward compatibility
}
if (!"POST".equals(method) && !"PUT".equals(method) &&
"http".equals(url.getProtocol())) {
throw new ProtocolException("HTTP method " + method +
" doesn't support output");
}
// if there's already an input stream open, throw an exception
if (inputStream != null) {
throw new ProtocolException("Cannot write output after reading
input.");
}
if (!checkReuseConnection())
connect();
/* REMIND: This exists to fix the HttpsURLConnection subclass.
* Hotjava needs to run on JDK.FCS. Do proper fix in subclass
* for . and remove this.
*/
if (streaming() && strOutputStream == null) {
writeRequests();
}
ps = (PrintStream)http.getOutputStream();
if (streaming()) {
if (strOutputStream == null) {
if (fixedContentLength != -) {
strOutputStream =
new StreamingOutputStream (ps, fixedContentLength);
} else if (chunkLength != -) {
strOutputStream = new StreamingOutputStream(
new ChunkedOutputStream (ps, chunkLength), -);
}
}
return strOutputStream;
} else {
if (poster == null) {
poster = new PosterOutputStream();
}
return poster;
}
} catch (RuntimeException e) {
disconnectInternal();
throw e;
} catch (IOException e) {
disconnectInternal();
throw e;
}
}
I have this problem too, what surprises me is that the error is caused by my added code System.out.println(conn.getHeaderFields());
Below is my code:
HttpURLConnection conn=(HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
configureConnection(conn);
//System.out.println(conn.getHeaderFields()); //if i comment this code,everything is ok, if not the 'Cannot write output after reading input' error happens
conn.connect();
OutputStream os = conn.getOutputStream();
os.write(paramsContent.getBytes());
os.flush();
os.close();
I had the same problem.
The solution for the problem is that you need to use the sequence
openConnection -> getOutputStream -> write -> getInputStream -> read
That means..:
public String sendReceive(String url, String toSend) {
URL url = new URL(url);
URLConnection conn = url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.sets...
OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream());
out.write(toSend);
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String receive = "";
do {
String line = in.readLine();
if (line == null)
break;
receive += line;
} while (true);
in.close();
return receive;
}
String results1 = sendReceive("site.com/update.php", params1);
String results2 = sendReceive("site.com/update.php", params2);
...
This is where I got the socketIO files from.
https://github.com/Gottox/socket.io-java-client/tree/master/src/io/socket
I am on the client side.
I know connecting works when the server does not need authentication.
But when it needs authentication (Username and password), I get a handshaking error message.
How do I get passed authentication?? Could it be a server side error? Would the server side of things change if authentication was added?
This is the function that throws an error...I did not write it.
This line is the one causing problems: InputStream stream = connection.getInputStream();
It says it is caused by: java.io.FileNotFoundException: url:80/socket.io/1/
private void handshake() {
URL url;
String response;
URLConnection connection;
try {
setState(STATE_HANDSHAKE);
url = new URL(IOConnection.this.url.toString() + SOCKET_IO_1);
connection = url.openConnection();
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection)
.setSSLSocketFactory(sslContext.getSocketFactory());
}
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(connectTimeout);
/* Setting the request headers */
for (Entry<Object, Object> entry : headers.entrySet()) {
connection.setRequestProperty((String) entry.getKey(),
(String) entry.getValue());
}
InputStream stream = connection.getInputStream();
Scanner in = new Scanner(stream);
response = in.nextLine();
String[] data = response.split(":");
sessionId = data[0];
heartbeatTimeout = Long.parseLong(data[1]) * 1000;
closingTimeout = Long.parseLong(data[2]) * 1000;
protocols = Arrays.asList(data[3].split(","));
} catch (Exception e) {
error(new SocketIOException("Error while handshaking", e));
}
}
Problem solved (sort of), here: Android developpement, Gottox socket.io-java-client: file not fount Exception /socket.io/1/
(try using an earlier version of socket.io - by first deleting socket.io folder from node_modules and then install an older version, e.g., 0.9.16, using this command: npm install socket.io#0.9.16)