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);
...
Related
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.
I have been running into a very strange problem. I am trying to implement log in service in my app. When I pass right email and password service returns response as expected(means no error comes) but when I delibrately pass wrong email or password geInputStream() method throws FileNotFoundException. I don't know what is the reason behind this.Further more, before calling getInputStream() method i checked status code as well(this is the case when I am passing wrong email and password intentionally).The status code was 500. I checked for 500 and that was internal server error. My question is why is that so? I mean when intentionally passing wrong email or password why internal server occurred? One more thing I would like to mention that I have checked the same service on post man it is working fine as expected. If i pass wrong email or password postman returns the expected error. Below is the code I am using
private String invokeWebservice() {
String data = null;
HttpURLConnection conn = null;
BufferedReader in = null;
try {
String webservice = Constants.BASE_URL + serviceName;
LogUtility.debugLog("webservice just called "+ webservice);
URL url = new URL(webservice);
conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setUseCaches(false);
if (isPost) {
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Accept", "application/json");
Writer writer = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), "UTF-8"));
if (jsonObject != null)
writer.write(jsonObject.toString());
writer.close();
}
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuffer sb = new StringBuffer("");
String l = "";
String nl = System.getProperty("line.separator");
while ((l = in.readLine()) != null)
sb.append(l + nl);
in.close();
data = sb.toString();
return data;
} catch (Exception e) {
LogUtility.errorLog("exception while calling web service");
} finally {
try {
if (conn != null)
conn.disconnect();
if (in != null)
in.close();
} catch (Exception ex) {
// LogUtility.errorLogWithException(ex, ex.getMessage());
}
}
return data;
}
Any help?
After spending some time now I was able to solve my problem.Posting my answer for others. Passing wrong email and password to the service was right and server was consuming those parameters as well and because there was an error(because email and password) that is why it was returning 500 code. So, I checked for status code if it was 200 then I used getInputStream() method and else i called getErrorStream() method. By this way i got the stream that has property for error(this property contains error detail). Below is the code i used
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
in = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
} else {
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
}
Hope it helps other as well.
I am new to this website, so if i do something wrong please tell me.
I am trying to establish a connection between my node.js server and my android app. For example, I'm trying to connect a page called showWithAuth, where i need to authenticate with digest stategy.
For this purpose i use Authenticator :
Authenticator.setDefault(new Authenticator()
{
#Override
protected PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication (username, password.toCharArray());
// System.out.println(pa.getUserName() + ":" + new String(pa.getPassword()));
}
});
My real issue is when i try to establish the connection :
try {
URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
in = new BufferedReader(new InputStreamReader(connection
.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
System.out.println(sb);
/*connection.setInstanceFollowRedirects(false);
int status = connection.getResponseCode();
InputStream is;
if (status >= 400 && status <= 499) {
throw new Exception("Bad authentication status: " + status); //provide a more meaningful exception message
}
else
{*/
//connection.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
//connection.setRequestProperty("Accept", "*/*");
/*is = connection.getInputStream();
}
byte[] buffer = new byte[8196];
int readCount;
final StringBuilder builder = new StringBuilder();
while ((readCount = is.read(buffer)) > -1) {
builder.append(new String(buffer, 0, readCount));
}
String response = builder.toString();
System.out.println(response);*/
} catch (java.net.ProtocolException e) {
sb.append("User Or Password is wrong!");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
The issue i have is a filenotfoundexception, at this line :.getInputStream()));
The response of the server is a 401 : bad authentication status
I saw some people having the same issue i deal with, but i tried every single solution without getting anything better.
If you could help me to get what i do wrong ! Thank you !
PS: the commented code was also tried.
PS2: sorry for being so long.
Edit: Just to say also that this code is working on Netbeans with Java only, but not in Android Studio
Please try by adding the
<uses-permission android:name="android.permission.INTERNET" />
To your AndroidManifest.xml file, this may solve your problem.
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().
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.