I'm trying to implement a little communication scheme handling HTTP-requests from an Android device to a Node.js server. With the current code the Android side closes the connection after receiving the response from the header.
Java:
public String doInBackground(Void... params) {
URL url = new URL("http://" + mServer.getHost() + ":" + mServer.getPort() + "/" + mPath);
HttpURLConnection http = (HttpURLConnection) url.openConnection();
http.setConnectTimeout(TIMEOUT);
http.setRequestMethod("POST");
http.setDoOutput(true);
http.connect();
OutputStream out = http.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(out);
writer.write(mJson);
writer.flush();
writer.close();
mResponseCode = http.getResponseCode();
if (mResponseCode != 200) {
http.disconnect();
return "";
}
InputStreamReader in = new InputStreamReader(http.getInputStream());
BufferedReader br = new BufferedReader(in);
char[] chars = new char[BUF_SIZE];
int size = br.read(chars);
String response = new String(chars).substring(0, size);
//http.disconnect();
return response;
}
Node:
this.socket = http.createServer((req, res) => {
req.on('data', (chunk) => {
this.log.info("DATA");
obj = JSON.parse(chunk.toString());
});
req.on('close', () => {
this.log.info("CLOSE");
});
req.on('connection', (socket) => {
this.log.info("CONNECTION");
});
req.on('end', () => {
this.log.info("END");
});
});
this.socket.listen(this.port, this.host);
Further the connection event on the Node side is never called, every request is directly piped into the data event.
Is there a way to establish a persistent HTTP-connection such that the Node server can keep track of it while the connection is running until the Android side closes it again?
Socket.io seems to be a reasonable library to achieve persistent connections from Android to a Node.js server.
Related
I am trying to connect to firebase to send push notification to an android app.
I have written following code in Java Server side. But I am getting connection timed out exception always.
final String apiKey="AAAAeEpqP-w:APA91bFulyT23Km-onTNr_q5yEz4uoOaM8KdE4LMyIoz6kWlk3pJSHirDJBSiqESRXKiGa-Z_tBfpXA6naaaTXxcFFxAnaSkMTPVVOMswyJ0bhhdpwlo-92HXgxRMsHV6Y8bNaHX7tMd";
int i=0;
try {
URL url = new URL("https://fcm.googleapis.com/fcm/send");
HttpsURLConnection conn = (HttpsURLConnection ) url.openConnection();
System.out.println("after connection open");
//conn.setDoInput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("Authorization", "key=" + apiKey);
conn.setDoOutput(true);
JSONObject json = new JSONObject();
json.put("to", "device-token");
JSONObject info = new JSONObject();
info.put("title", "notification title"); // Notification title
info.put("body", "message body");
json.put("notification", info);
OutputStreamWriter wr = new OutputStreamWriter(
conn.getOutputStream());
wr.write(json.toString());
wr.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
String output;
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
System.out.println(output);
}
// result = CommonConstants.SUCCESS;
System.out.println("after closed out put stream");
int responseCode = conn.getResponseCode();
// System.out.println("Post parameters : " + input);
System.out.println("Response Code : " + responseCode);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch(Exception e){
e.printStackTrace();
}
}
It always shows session timeout error.
Is it a correct way to send notifications to app.
Any help is appreciated.
EDIT :- I have called the same url with device token and other things from rest client and it works well.I got a notification in my app.But when i send it thru Java code at that time it shows connection timed out.
Here's the official Firebase documentation which can help you set up the server. https://firebase.google.com/docs/cloud-messaging/server
In your code I noticed json.put("to", "device-token"); and I am assuming that you are using it as a placeholder only for the example. If not, in your request you need to send a device-token like { "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..." } .
I have very simple java server:
int port = 2245;
try {
ServerSocket ss = new ServerSocket(port);
System.out.println("Waiting for a client...");
Socket incomingClient = ss.accept();
InputStream i = incomingClient.getInputStream();
OutputStream o = incomingClient.getOutputStream(); // Use it write to the Client Socket
InputStreamReader isr = new InputStreamReader(i);
BufferedReader br = new BufferedReader(isr);
String str = new String();
while ((str = br.readLine())!=null){
System.out.println("str = " + str);
o.write(123); //("message from server");
}
} catch(Exception x) { x.printStackTrace(); }
And I have simple Qt client, that use QNetworkAccessManager
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QUrl url("http://127.0.0.1:2245");
url.port(6666);
QByteArray postData;
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QString postKey("SomeKey");
postData.append(postKey);
QObject::connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
I declared in mainwindow.h in slot replyFinished
public slots:
void replyFinished(QNetworkReply* reply);
In best case Server get some headers like(POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded and others)
But on server I can not read my message from client
and client does not receive ANY response from java server
The correct code for Qt client is this:
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QUrl url("http://192.168.0.101:8000");
url.port(8000);
QByteArray postData;
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QString postKey("some data");
postData.append(postKey);
QObject::connect(manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(replyFinished(QNetworkReply *)));
manager->post(request, postData);
}
and to get response declare and implement the following slot
void MainWindow::replyFinished(QNetworkReply *reply){
qDebug() << "Status" << reply->errorString();
qDebug() << "Status" << reply->error();
QByteArray data = reply->readAll(); //It's works!
qDebug() << "data: " << data;
}
For server side I have used HttpServer, which is available in latest version of JDK
I am retrieving data through an API using HTTP Basic Authentication in the following way:
for (int i = 1; i < 10000; i++) {
try {
URL newurl = new URL ("SOMEURL" + i + ".json");
HttpURLConnection newconnection = (HttpURLConnection) newurl.openConnection();
newconnection.setRequestMethod("GET");
newconnection.setDoInput(true);
newconnection.setRequestProperty ("Authorization", "Basic " + encoding);
try {
InputStream newcontent = (InputStream)newconnection.getInputStream();
BufferedReader bfReader = new BufferedReader (new InputStreamReader (newcontent));
String newline;
if ((newline = bfReader.readLine()) != null) {
out.println(newline);
}
}
catch(Exception e2) {
}
}
catch(Exception e) {
e.printStackTrace();
}
}
Question: Instead of asking for authentication 10,000 times, can I get the authentication once for the first request, keep it alive, and make the remaining 9999 requests without asking for authentication?
Purpose: Faster processing, less load on the server.
Any comments?
HTTP is designed to be stateless, so there is nothing wrong with your approach. However, you can (but not recommened with an API) you sessions (with cookies) or provide a login URL which will issue a temporary auth token which you can pass.
I have written a demo in Java that posts data once a second:
public static void main(String[] arystrArgs) {
//Get Operating System name and version
String strOSname = System.getProperty("os.name").toLowerCase();
//Display application title and what it is running on
System.out.println("Java Data Posting Demo");
System.out.println("Build date: " + BUILD_DATE + ", Version: " + VERSION_NO);
System.out.println("Running on: " + strOSname.substring(0, 1).toUpperCase()
+ strOSname.substring(1).toLowerCase());
//Post data to server
HttpURLConnection conn = null;
while( true ) {
try {
Thread.sleep(DELAY_BETWEEN_POSTS);
URL url = new URL("http://localhost:8080");
conn = (HttpURLConnection)url.openConnection();
if ( conn != null ) {
//Whatever you wants to post...
String strPostData = "p1=Hello&p2=" + (new Date()).getTime();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-length", Integer.toString(strPostData.length()));
conn.setRequestProperty("Content-Language", "en-GB");
conn.setRequestProperty("charset", "utf-8");
conn.setUseCaches(false);
conn.setDoOutput(true);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(strPostData);
dos.close();
System.out.println("Post to: " + url.toString() + ", data: " + strPostData);
}
} catch (InterruptedException|IOException ex) {
//ex.printStackTrace();
} finally {
if ( conn != null ) {
conn.disconnect();
conn = null;
}
}
}
}
I have written a Node.js application that listens on port 8080, but I don't see any POST requests in the http handler, I only see GET requests when I use a browser on the same address and port to test.
Snippet from node.js application:
function defaultHandler(request, response) {
try{
if ( request.method == "POST" ) {
var strBody = "";
request.on("data", function(chunk) {
strBody += chunk;
});
request.on("end", function() {
console.log("Received posted data: " + strBody);
});
} else {
console.dir(request);
}
} catch( ex ) {
console.dir(ex);
}
};
var app = http.createServer(defaultHandler);
app.listen(8080);
This is a cut down version, but all I ever see is get requests. I can see that the Java is connecting and posting data, as when I start Node.js and only when I start Node.js, the Java application connects to the URL then starts POSTING with a second delay between posts, it I terminate node, then it stops posting and restarting node causes the posts to resume.
Your Node app never send response to client. You send a lot of request but Java Client never receive response from server. You should execute end method on response.
var http = require('http');
function defaultHandler(request, response) {
try {
if (request.method == "POST") {
var strBody = "";
request.on("data", function(chunk) {
strBody += chunk;
});
request.on("end", function() {
console.log("Received posted data: " + strBody);
});
} else {
console.dir(request);
}
respone.end(); // for example here
} catch (ex) {
console.dir(ex);
}
};
var app = http.createServer(defaultHandler);
app.listen(8080);
Here you can find documentation - https://nodejs.org/api/http.html#http_class_http_serverresponse
I'm not a Java developer, but I think you can try use flush and getResponseCode method on connection.
conn.setDoOutput(true);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(strPostData);
dos.flush();
dos.close();
int responseCode = conn.getResponseCode();
Fixed, it was the Java, I modified the Java code as follows:
URL url = new URL("http://localhost:8080");
conn = (HttpURLConnection)url.openConnection();
if ( conn != null ) {
//Whatever you wants to post...
String strPostData = "p1=Hello&p2=" + (new Date()).getTime();
conn.setRequestMethod("POST");
conn.setRequestProperty("User-Agent", USER_AGENT);
conn.setRequestProperty("Accept-Language", "en-GB,en;q=0.5");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Content-length", Integer.toString(strPostData.length()));
conn.setRequestProperty("Content-Language", "en-GB");
conn.setRequestProperty("charset", "utf-8");
conn.setUseCaches(false);
conn.setDoOutput(true);
DataOutputStream dos = new DataOutputStream(conn.getOutputStream());
dos.writeBytes(strPostData);
dos.flush();
dos.close();
int intResponse = conn.getResponseCode();
System.out.println("\nSending 'POST' to " + url.toString() +
", data: " + strPostData + ", rc: " + intResponse);;
}
(This is for a signed applet and I have decided against HTTPClient to keep my jar very small)
I am using HttpURLConnection to successfully upload a file from the user to a server using multi-part form post.
The problem is that HttpURLConnection is caching the data -- before sending it. So when I am reading from the file and writing to Outputstream, it is merely buffering the data -- and therefore my progress bar, that shows the upload status , is completely wrong. Howevere please note that the form post code works and the file does get uploaded correctly with return code of 200.
So how do I ensure that HttpURLConnection does not cache the data that I am sending to the server ?
Here is my source:
public UploadResponse send(String formPostUrlStr,String fileFieldName,File targetFile, Map<String, String> valuesMap, UploadStatusListener uploadStatusListener) throws Exception{
String sendStr=getBoundaryMessage(Boundary, valuesMap, fileFieldName, targetFile.getName(), valuesMap.get("content-type") );//"image/png") ;
System.out.println(" multi-part start \n "+ sendStr+ " multi-part end \n");
String lenstr=Long.toString((long)(sendStr.length()*2)+ targetFile.length());
System.out.println("Content-Length"+ lenstr);
//Content-Length
URL url= new URL(formPostUrlStr);
long startTime= System.currentTimeMillis();
HttpURLConnection s3Connection = (HttpURLConnection) url.openConnection();
System.out.println("opened url to "+ formPostUrlStr +", connection ok ..");
s3Connection.setRequestProperty("Content-Type", "multipart/form-data; boundary="
+ Boundary);
s3Connection.setRequestProperty("content-length", lenstr);
s3Connection.setDoOutput(true);
s3Connection.setDoInput(true);
s3Connection.setUseCaches(false);
s3Connection.setInstanceFollowRedirects(true);
s3Connection.setAllowUserInteraction(true);
s3Connection.setRequestProperty("User-Agent", "Mozilla/4.5");
if (uploadStatusListener != null) {
uploadStatusListener.statusUpdate(targetFile.length(), 0);
}
String debugStr= s3Connection.toString();
System.out.println("conmnection "+ debugStr);
DataOutputStream httpOut = new DataOutputStream(s3Connection.getOutputStream());
System.out.println("opened DataOutputStream ok ..");
httpOut.write(sendStr.getBytes());
//httpOut.flush();
System.out.println("httpOut.flush 1 ok ..");
FileInputStream uploadFileReader = new FileInputStream(targetFile);
long totalBytes = uploadFileReader.available();
if (uploadStatusListener != null) {
uploadStatusListener.statusUpdate(totalBytes, 0);
}
System.out.println(" uploading file with size "+ uploadFileReader.available());
int bufSize = 102400;
long availableBytesToRead;
long totalSent = 0;
while ((availableBytesToRead = uploadFileReader.available()) > 0) {
byte[] bufferBytesRead;
bufferBytesRead = availableBytesToRead >= bufSize ? new byte[bufSize]
: new byte[(int)availableBytesToRead];
int count = uploadFileReader.read(bufferBytesRead);
try{
httpOut.write(bufferBytesRead);
totalSent += ((long) count);
System.out.println(" wrote bytes = "+count+ ", total sent = "+ totalSent +", pendingSize"+ (availableBytesToRead-count) );
}
catch(IOException ioe){
System.out.println(" io exceotion e"+ ioe.getMessage());
throw ioe;
}
//httpOut.flush();
if (uploadStatusListener != null) {
uploadStatusListener.statusUpdate(totalBytes, totalSent);
}
}
// FILE DATA END
httpOut.write(("--" + Boundary + "--\r\n").getBytes());
// form end
httpOut.write(("--" + Boundary + "--\r\n").getBytes());
httpOut.flush();
httpOut.close();
long endTime= System.currentTimeMillis();
System.out.println("Completed Writing Data to S3 Connection in "+ (endTime-startTime)+"ms.,now waiting for rsponse code ");
int code=s3Connection.getResponseCode();
long endTime2= System.currentTimeMillis();
System.out.println("Completed Sendind Data to S3 in "+ (endTime2-startTime)+ "ms., rsponse code time "+ (endTime2-endTime)+"ms. ");
UploadResponse uploadResponse = new UploadResponse();
uploadResponse.setCode(code);
System.out.println(" response code : " + code);
StringBuilder response = new StringBuilder();
byte[] respBuffer = new byte[4096];
if (code > 300) {
if (code == 404) {
throw new Exception("Error 404");
}
BufferedReader err = new BufferedReader(
new InputStreamReader(s3Connection.getErrorStream()));
String ret;
StringBuffer buff = new StringBuffer();
while ((ret = err.readLine()) != null) {
buff.append(ret);
}
uploadResponse.setMessage(buff.toString());
System.out.println(" error :"+ buff.toString());
err.close();
} else {
BufferedReader inp = new BufferedReader(
new InputStreamReader(s3Connection.getInputStream()));
StringBuffer buff = new StringBuffer();
String ret;
while ((ret = inp.readLine()) != null) {
buff.append(ret);
}
inp.close();
uploadResponse.setMessage(buff.toString());
if(buff.toString().contains("fail"))
throw new Exception("Upload failed");
}
System.out.println(response.toString());
return uploadResponse;
}
}
I have the same problem.
I didn't find any other solution than writing my HTTP request on a raw Socket.
Did you find a better workaround ?
EDIT : I just did : we just have to use obj.setFixedLengthStreamingMode(12345) on the HttpURLConnection object obtained from url.openConnection(), where 12345 is the length of POST request body.
As a complementation for the answer that #Antares gave, there is another method setChunkedStreamingMode that is used when you don't know the content size in advance. So when you do a POST request, call that method on the connection:
HttpURLConnection connection = ...
connection.setRequestMethod("POST");
connection.setDoOutput(true);
connection.setChunkedStreamingMode(0);
connection.connect();
... connection.getOutputStream();
This will avoid the OutputStream to buffer the entire content before start to send.