node.js http server concurrency issue - java

Whenever I try to to send rapid HTTP Post in succession, the server sometimes crashes (java.net.ConnectException: Connection refused: connect), sometimes freezes (no error, but cannot use it anymore), and sometimes works....
If I send HTTP Post to the server slowly, there seems to be no problems at all.
I have the simple code for an http server in node.js - My guess is that sometimes the NodeJS server will receive a request, then receive another request before it sends out the respond, thus causing all sorts of problem. How do I make my server able to accept multiple requests all at once?
var server = http.createServer(function (req, res) {
if (req.method != 'POST') {
res.end();
}
else {
req.on('data', function(chunk) {
//Do some stuff here
file1=JSON.parse(chunk.toString());
console.log("Hello World") ;
}
req.on('end', function() {
res.writeHead(200, "OK", {'Content-Type': 'text/html'});
res.end();
});
}
} server.listen(9000);
EDIT
Here is the java program sending the HTTP POSTS
public static String httpUrlRequest(String requestURL, String json) {
URL url;
String response = "";
HttpURLConnection connection = null;
InputStream is = null;
try {
url = new URL(requestURL);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.getOutputStream().write(json.getBytes());
connection.getOutputStream().flush();
connection.getOutputStream().close();
int code = connection.getResponseCode();
System.out.println("code" + code);
} catch (Exception e) {
e.printStackTrace();
} finally {
connection.disconnect();
}
return response;
}
public static void main(String[] args) {
Date date = new Date();
Gson gson = new Gson();
Map<String, Object> tempMap = gson.fromJson(json, new TypeToken<Map<String, Object>>(){}.getType());
for(int i = 0; i < 10; i++){
date = new Date();
tempMap.put("GetOn", getDateString(date));
httpUrlRequest("http://111.111.11.111:9000" ,gson.toJson(tempMap));
}
}
UPDATE :
If I parse the JSON in the nodejs server, then sometimes I get the error where the connection is refused.
So when I parse the request data, for some reason, nodejs fails to receive the entire json file that was sent (5KB). Instead, it only receives half of it, and my Java console says connect error. And this problem occurs AFTER nodejs correctly parses about 3 to 5 requests. Then on the next request, everything goes wrong. How can I tell if the java breaks connection causing only half of the JSON to be sent, or if only half of the JSON is sent causing nodejs to crash, ultimately resulting in connect error.
If I comment out all the parsing, then I never get the errors anymore. I don't even understand why JSON.Parse in nodejs would induce a java connect error....

Your problem lies here:
req.on('data', function(chunk) {
//Do some stuff here
file1=JSON.parse(chunk.toString());
console.log("Hello World") ;
}
Since the body can be sent in multiple packets, you might receive more than 1 'data' event, thus attempting to parse an incomplete JSON. Try this instead:
var chunks = [];
req.on('data', function(chunk) {
chunks.push(chunk);
}
req.on('end', function () {
// assemble all chunks
var body = Buffer.concat(chunks).toString(),
file1 = JSON.parse(body);
res.writeHead(200, "OK", {'Content-Type': 'text/html'});
res.end();
});

during this kind of tests, it would be easy to hit max throughput via the connection port.
http://www.slideshare.net/sh1mmer/a-million-connections-and-beyond-nodejs-at-scale
https://groups.google.com/forum/#!topic/nodejs/cRRS7ZJkyzc
The simply solution is ulimit -n 100000 before your run your node.js. But never do this in the production, better consider cluster for really large connection if you need to handle it.

Related

Subscribing to RESTful API and getting notifications

I'm having trouble receiving notification from API in JSON format. I've made a SpringBoot application that gets entities from the URL from the server (port:1026). However, the API has a subscription and notification system that I am supposed to utilize.
I'm having trouble realizing the implementation of getting the notification from API. When I subscribe to API a JSON entity is sent that I'm subscribing to I send an endpoint URL (localhost on port:1028) on which the notification is being sent. (entity and endpoint are in the same POST request to API to subscribe).
The issue is I don't know how to listen to that notification and show it on a webpage so when a call is made on API for value of that entity to change I see the notification on server log and see it in real time on my browser webpage.
This is the code that needs to be reworked. Here I just get a GET call from API to see what entities are created but when I make a PUT/POST to API via postman, manual refreshing is needed in order to see the change, and it's not utilizing the subscription system.
I think I need some kind of GET listener from server (localhost:1026) in order to parse the entity.
try {
URL url = new URL("http://localhost:1026/v2/entities");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.connect();
//Check if connection is made
int responseCode = conn.getResponseCode();
// 200 OK
if (responseCode != 200) {
throw new RuntimeException("HttpResponseCode: " + responseCode);
} else {
informationString = new StringBuilder();
Scanner scanner = new Scanner(url.openStream());
while (scanner.hasNext()) {
informationString.append(scanner.nextLine());
logger.info("Entity updated");
}
//Close the scanner
scanner.close();
logger.info(String.valueOf(informationString));
//return String.valueOf(informationString);
}
} catch (Exception e) {
e.printStackTrace();
}
return String.valueOf(informationString);
You can use setTimeout() on the webpage to periodically call your API then display the response immediately

Can't send an error body on the servlet output

I have the following code that is supposed to send a message to the client (I'm implementing a low level proxy):
//I made an httpurl connection to contact the remote server
httpServletResponse.setStatus(httpURLConnection.getResponseCode());
InputStream servletStream = getResponseInputStream(httpURLConnection); //I take either the output or error stream by evaluating the return code
IOUtils.copy(servletStream, httpServletResponse.getOutputStream());
httpServletResponse.flushBuffer();
private InputStream getResponseInputStream(HttpURLConnection httpURLConnection) throws IOException {
if (httpURLConnection.getResponseCode() < 400) {
return httpURLConnection.getInputStream();
}else {
return httpURLConnection.getErrorStream();
}
}
Now, if the response from remote server is 200 everything works fine.
If the response is 500 I can't see the body on my postman. Why is that?

Android HttpURLConnection receives HTTP 301 response code

I'm trying to do a HTTP GET using the HttpURLConnection object in Android.
UPDATE
I tried connection to a different server. This is also hosted within Cloud 9 (c9.io) and also returns a json response. This time I'm not getting a 301 redirect, but I am getting the actual response the server is supposed to send.
Since this means the problem is localised within the server, I've reorganized the following sections in order to focus reading onto the server-related information. Android related information has been moved to the end of the question.
Where I am connecting:
Development server on Cloud9
Using the Laravel Framework 5.2 (we cannot upgrade to 5.3 at this time, due to unsupported project dependencies)
The server should return a JSON answer
If I connect to the same URL through the browser I get the correct response (JSON string. Required HTTP Headers and a '200' HTTP Response Code)
Where I am connecting FROM
Android phone (Oneplus 3, on Android 6.0)
Compile SDK version: 23
Using Build Tools: "23.0.3"
Using Min SDK verion: 19
Using Target SDK version: 22
I'm connectiong using a HttpURLConnection object, using HTTP Method 'GET'
HTTP Response on Android
When I run my code I get the folling result from the server:
The HTTP response code is 301 but the message is null.
The new URL is exactly the same, but using HTTPS. It seems server is somehow forcing SSL/TSL encryption. Which does not happen when accessing HTTP from the browser.
HTTP Header (on Android):
date => Tue, 04 Oct 2016 05:56:26 GMT
location => https://domain.com/route/ (I modified this bit)
content-length => 382
content-type => text/html; charset=iso-8859-1
X-BACKEND => apps-proxy
X-Android-Selected-Protocol => http/1.1
X-Android-Sent-Millis => 1475560583894
X-Android-Received-Millis => 1475560585637
X-Android-Response-Source => NETWORK 301
null => HTTP/1.1 301
Other data
Since it seems the server wants Android to use HTTPS, I tried modifying the code to use HTTPS (HttpsURLConnection). This may or may not solve this problem, but I am unable to check it since I get an annoying SSL handshake failed error. Plus I have no need for encryption on this application, and therefore I'm reluctant to solve the problems coming with it.
This is all running within an AsyncTask object (since Android get moody when you try to use a network connection on the main thread).
Setting up a new server (outside of Cloud 9 and without any SSL/TSL) could be an option, but I'm reluctant to do this since it would be quite time consuming.
I tried connecting to another Cloud 9 server (which also returns a json response), using the exact same code, and everything works correctly. This suggests that the problem arises from the HTPP 301 error.
I will try to share with you any other information you may require to answer my question!
Native Android stuff (moved on UPDATE, see above)
The response content seems to be an incomplete JSON:
{ 'status':'ERROR'
Note I did NOT forget the closing } character, that's what the response actually containts. This is injected somewhere unknown (to me) during the workflow. When I capture the HTTP response (using Charles on my PC, which is set as a Proxy for my phone's Wi-Fi connection) it's content is (as expected) a simple HTML telling you to redirect (HTPP code 301) to a new route.
The invalid JSON code (above) isn't there, but a valid HTML is.
This would suggest that the invalid JSON appears somewhere internally to my code (not on the server, or transport). But there is no code on my app that generates a JSON string, let alone inject it into the response I'm processing.
Code for the HttpURLConnection
this.setURL(ruta); //gets correct url
HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection(); //init
cxn.setRequestMethod("GET"); //use HTTP GET verb
cxn.setUseCaches(false); //no cache
cxn.setRequestProperty("Cache-Control", "no-cache"); //even less cache
cxn.setDoOutput(false); //only true in POST/PUT requests
cxn.setRequestProperty("Connection","keep-alive");
cxn.setRequestProperty("DNT", "1"); //TEMP
cxn.setInstanceFollowRedirects(true); //should follow redirects
cxn.setRequestProperty( "charset", "utf-8");
Code for the reading the result
int status_code = cxn.getResponseCode();
InputStream responseStream = new BufferedInputStream(cxn.getInputStream());
BufferedReader responseStreamReader = new BufferedReader(new InputStreamReader(responseStream));
String line = "";
StringBuilder stringBuilder = new StringBuilder();
while ((line = responseStreamReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
responseStreamReader.close();
String response = stringBuilder.toString();
cxn.disconnect();
Remove the code you've used to create the HttpURLConnection and try with this one:
URL url;
HttpURLConnection urlConnection = null;
try {
url = new URL("http://www.domain.com/index.aspx?parameter1=X&parameter2=X"); //Use your url and add the GET parameters
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setInstanceFollowRedirects(false); /* added line */
InputStream in = urlConnection.getInputStream();
InputStreamReader isw = new InputStreamReader(in);
int data = isw.read();
while (data != -1) {
char current = (char) data;
data = isw.read();
System.out.print(current);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
This should be all you need to set for your GET request.
EDIT:
I've tested the webservice using Volley, here's the code I've used in order to retrieve the webservice response:
public class MainActivity extends AppCompatActivity {
public String response;
TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.rTextView);
RequestQueue queue = Volley.newRequestQueue(this);
String url = "yourWebserviceUrl";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener < String > () {
#Override
public void onResponse(String response) {
textView.setText("Response is: " + response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
textView.setText("That didn't work!");
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
}
And this is the response I got:
{"status":"ok","found":false,"extra":"App\\Scanners"}
Changing the protocol to https worked for me.
I faced the same problem, and I fixed it after reading this source.
All we need to do is handling 3** errors like shown below
if(responseCode > 300 && responseCode < 400) {
String redirectHeader = conn.getHeaderField("Location");
if(TextUtils.isEmpty(redirectHeader)) {
return new JsonResponse(responseCode, "Failed to redirect");
}
JsonRequest newRequest = request;
newRequest.url = redirectHeader;
return getJsonFromUrl(newRequest);
}
Each 3** response should have a header with name Location which contains a redirect link which we should use.
Change the line :
HttpURLConnection cxn = (HttpURLConnection) this.getURL().openConnection();
with :
HttpsURLConnection cxn = (HttpsURLConnection) this.getURL().openConnection();
So you will able to handle https

java.io.InputStream will not throw IOException when Wifi connectivity is lost

I am running into an issue with some code in my Android application which downloads a file from a URL, here is a code snippet:
int bytesRead = 0;
final byte[] buffer = new byte[32 * 1024];
InputStream stream = httpUrlConnection.getInputStream();
try {
while ((bytesRead = stream.read(buffer)) > 0) {
Log.i("TAG", "Read from steam, Bytes Read: " + bytesRead);
}
} catch (IOException ex) {
//Recover from lost WIFI connection.
} finally {
stream.close();
}
My application relies on InputStream.read() to throw an IOException if WiFi connectivity is lost. As stated in the Java 8 documentation this method should throw an IOException "if the input stream has been closed, or if some other I/O error occurs". In Android M, this occurs immediately and my code can process and recover from the exception. In Android N, this exception is not thrown which causes my app to simply hang in the read() method, it never breaks out of it. Has anyone else run into this problem and worked around it in such a way that doesn't break backwards compatibility? Is this a new Android N bug?
Reading from a socket can block forever if the connection goes down. You need to use a read timeout.
To avoid reinventing the wheel and find yourself figuring out this and some other common scenarios, I would use a high library such as Volley
see: Transmitting Network Data Using Volley
Volley offers you in a very straight forward way interesting things as:
timeout control
Ease of customization, for example, for retry and backoff
control of several kind of errors
cancellation request API. You can cancel a single request, or you can set blocks or scopes of requests to cancel.
Debugging and tracing tools.
etc
Sending a request is as easy as something like this (from the docs )
final TextView mTextView = (TextView) findViewById(R.id.text);
...
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
mTextView.setText("Response is: "+ response.substring(0,500));
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
mTextView.setText("That didn't work!");
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
Setting a timeout/retry policy is as easy as:
stringRequest.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
where the parameters are:
Timeout: Specifies Socket Timeout in millis per every retry attempt.
Number Of Retries: Number of times retry is attempted.
Back Off Multiplier: A multiplier which is used to determine exponential time set to socket for every retry attempt.
Regarding error handling
As you see, you are passing an error listener new Response.ErrorListener() to the request. When there is an error, Volley invokes the onErrorResponse callback public void onErrorResponse method passing an instance of the VolleyError object when there is an error while performing the request.
The following is the list of exceptions in Volley, taken from this post here
AuthFailureError — If you are trying to do Http Basic authentication then this error is most likely to come.
NetworkError — Socket disconnection, server down, DNS issues might result in this error.
NoConnectionError — Similar to NetworkError, but fires when device does not have internet connection, your error handling logic can club
NetworkError and NoConnectionError together and treat them similarly.
ParseError — While using JsonObjectRequest or JsonArrayRequest if the received JSON is malformed then this exception will be generated. If you get this error then it is a problem that should be fixed instead of being handled.
ServerError — The server responded with an error, most likely with 4xx or 5xx HTTP status codes.
TimeoutError — Socket timeout, either server is too busy to handle the request or there is some network latency issue. By default Volley times out the request after 2.5 seconds, use a RetryPolicy if you are consistently getting this error.
So you can do things like
if ((error instanceof NetworkError) || (error instanceof NoConnectionError)) {
//then it was a network error
}
As #EJP says "Reading from a socket can block forever if the connection goes down", just add this line to your code , and also catch java.net.SocketTimeoutException :
int bytesRead = 0;
final byte[] buffer = new byte[32 * 1024];
InputStream stream = httpUrlConnection.getInputStream();
httpUrlConnection.setConnectTimeout(5000);
try {
while ((bytesRead = stream.read(buffer)) > 0) {
Log.i("TAG", "Read from steam, Bytes Read: " + bytesRead);
}
} catch (java.net.SocketTimeoutException ex) {
//Recover from lost WIFI connection.
} finally {
stream.close();
}

FileNotFoundException while getting the InputStream object from HttpURLConnection

I am trying to send a post request to a url using HttpURLConnection (for using cUrl in java).
The content of the request is xml and at the end point, the application processes the xml and stores a record to the database and then sends back a response in form of xml string. The app is hosted on apache-tomcat locally.
When I execute this code from the terminal, a row gets added to the db as expected. But an exception is thrown as follows while getting the InputStream from the connection
java.io.FileNotFoundException: http://localhost:8080/myapp/service/generate
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1401)
at org.kodeplay.helloworld.HttpCurl.main(HttpCurl.java:30)
Here is the code
public class HttpCurl {
public static void main(String [] args) {
HttpURLConnection con;
try {
con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setDoInput(true);
File xmlFile = new File("test.xml");
String xml = ReadWriteTextFile.getContents(xmlFile);
con.getOutputStream().write(xml.getBytes("UTF-8"));
InputStream response = con.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(response));
for (String line ; (line = reader.readLine()) != null;) {
System.out.println(line);
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Its confusing because the exception is traced to the line InputStream response = con.getInputStream(); and there doesn't seem to be any file involved for a FileNotFoundException.
When I try to open a connection to an xml file directly, it doesn't throw this exception.
The service app uses spring framework and Jaxb2Marshaller to create the response xml.
The class ReadWriteTextFile is taken from here
Thanks.
Edit:
Well it saves the data in the DB and sends back a 404 response status code at the same time.
I also tried doing a curl using php and print out the CURLINFO_HTTP_CODE which turns out to be 200.
Any ideas on how do I go about debugging this ? Both service and client are on the local server.
Resolved:
I could solve the problem after referring to an answer on SO itself.
It seems HttpURLConnection always returns 404 response when connecting to a url with a non standard port.
Adding these lines solved it
con.setRequestProperty("User-Agent","Mozilla/5.0 ( compatible ) ");
con.setRequestProperty("Accept","*/*");
I don't know about your Spring/JAXB combination, but the average REST webservice won't return a response body on POST/PUT, just a response status. You'd like to determine it instead of the body.
Replace
InputStream response = con.getInputStream();
by
int status = con.getResponseCode();
All available status codes and their meaning are available in the HTTP spec, as linked before. The webservice itself should also come along with some documentation which overviews all status codes supported by the webservice and their special meaning, if any.
If the status starts with 4nn or 5nn, you'd like to use getErrorStream() instead to read the response body which may contain the error details.
InputStream error = con.getErrorStream();
FileNotFound is just an unfortunate exception used to indicate that the web server returned a 404.
To anyone with this problem in the future, the reason is because the status code was a 404 (or in my case was a 500). It appears the InpuStream function will throw an error when the status code is not 200.
In my case I control my own server and was returning a 500 status code to indicate an error occurred. Despite me also sending a body with a string message detailing the error, the inputstream threw an error regardless of the body being completely readable.
If you control your server I suppose this can be handled by sending yourself a 200 status code and then handling whatever the string error response was.
For anybody else stumbling over this, the same happened to me while trying to send a SOAP request header to a SOAP service. The issue was a wrong order in the code, I requested the input stream first before sending the XML body. In the code snipped below, the line InputStream in = conn.getInputStream(); came immediately after ByteArrayOutputStream out = new ByteArrayOutputStream(); which is the incorrect order of things.
ByteArrayOutputStream out = new ByteArrayOutputStream();
// send SOAP request as part of HTTP body
byte[] data = request.getHttpBody().getBytes("UTF-8");
conn.getOutputStream().write(data);
if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "http response code is " + conn.getResponseCode());
return null;
}
InputStream in = conn.getInputStream();
FileNotFound in this case was an unfortunate way to encode HTTP response code 400.
FileNotFound in this case means you got a 404 from your server - could it be that the server does not like "POST" requests?
FileNotFound in this case means you got a 404 from your server
You Have to Set the Request Content-Type Header Parameter
Set “content-type” request header to “application/json” to send the request content in JSON form.
This parameter has to be set to send the request body in JSON format.
Failing to do so, the server returns HTTP status code “400-bad request”.
con.setRequestProperty("Content-Type", "application/json; utf-8");
Full Script ->
public class SendDeviceDetails extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
String data = "";
String url = "";
HttpURLConnection con = null;
try {
// From the above URL object,
// we can invoke the openConnection method to get the HttpURLConnection object.
// We can't instantiate HttpURLConnection directly, as it's an abstract class:
con = (HttpURLConnection)new URL(url).openConnection();
//To send a POST request, we'll have to set the request method property to POST:
con.setRequestMethod("POST");
// Set the Request Content-Type Header Parameter
// Set “content-type” request header to “application/json” to send the request content in JSON form.
// This parameter has to be set to send the request body in JSON format.
//Failing to do so, the server returns HTTP status code “400-bad request”.
con.setRequestProperty("Content-Type", "application/json; utf-8");
//Set Response Format Type
//Set the “Accept” request header to “application/json” to read the response in the desired format:
con.setRequestProperty("Accept", "application/json");
//To send request content, let's enable the URLConnection object's doOutput property to true.
//Otherwise, we'll not be able to write content to the connection output stream:
con.setDoOutput(true);
//JSON String need to be constructed for the specific resource.
//We may construct complex JSON using any third-party JSON libraries such as jackson or org.json
String jsonInputString = params[0];
try(OutputStream os = con.getOutputStream()){
byte[] input = jsonInputString.getBytes("utf-8");
os.write(input, 0, input.length);
}
int code = con.getResponseCode();
System.out.println(code);
//Get the input stream to read the response content.
// Remember to use try-with-resources to close the response stream automatically.
try(BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"))){
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (con != null) {
con.disconnect();
}
}
return data;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Log.e("TAG", result); // this is expecting a response code to be sent from your server upon receiving the POST data
}
and call it
new SendDeviceDetails().execute("");
you can find more details in this tutorial
https://www.baeldung.com/httpurlconnection-post
The solution:
just change localhost for the IP of your PC
if you want to know this: Windows+r > cmd > ipconfig
example: http://192.168.0.107/directory/service/program.php?action=sendSomething
just replace 192.168.0.107 for your own IP (don't try 127.0.0.1 because it's same as localhost)
Please change
con = (HttpURLConnection) new URL("http://localhost:8080/myapp/service/generate").openConnection();
To
con = (HttpURLConnection) new URL("http://YOUR_IP:8080/myapp/service/generate").openConnection();

Categories

Resources