I am running a Jetty web server and using it to execute some bash scripts depending on what parameters it receives via GET. I've never worked with Jetty, so I pieced together some stuff from here to get it working. My issue is that it idles at ~100mb of ram when there are no requests, and after about 12 hours, it idles as high as 1gb of ram. This seems like an awful lot for something that just runs a bash script if the request is legit. The average file size that the servlet ends up serving is about 400kb.
How the server is started:
public void startServer() {
String hostname = "localhost";
int port = 7500;
Server server = new Server();
Connector connector = new SelectChannelConnector();
connector.setHost(hostname);
connector.setPort(port);
server.setConnectors(new Connector[]{connector});
server.setStopAtShutdown(true);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
context.addServlet(new ServletHolder(new DaemonServlet()), "/call/*");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { context });
server.setHandler(handlers);
server.start();
server.join();
}
The servlet (DaemonServlet):
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String msg = "";
String call = request.getParameter("call");
ProcessBuilder pb = null;
Process p = null;
switch (call) {
case "sendAction": // Sends an action to a process with a certain ID
pb = new ProcessBuilder("/bin/bash", "/opt/test/process.sh", request.getParameter("process"), request.getParameter("action"));
break;
case "getFile": // Reads the file with the given ID into a string, to be returned by Jetty
pb = new ProcessBuilder("/bin/bash", "/opt/test/getFile.sh", request.getParameter("fileId"));
try {
p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String scriptOutput = "";
String line;
while ((line = br.readLine()) != null) {
scriptOutput += line + '\n';
}
br.close();
msg = scriptOutput;
p.destroy();
p = null;
} catch (Exception e) {}
break;
}
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println(msg);
}
Is there anything that seems to be set up incorrectly here?
It looks like the line:
scriptOutput += line + '\n';
is consuming a huge amount of memory. Why not write the information back through the response as you get it?
As Reimeus said, you should probably echo the output as you get it. If you go that way, you should send the response headers (content type, status code) before sending the process output.
At the very least, you should use StringBuilder instead of concatenating Strings.
Related
I have a TCP client application in Java, through this application i can communicate with a server application.
I have a simple method sendCommand which sends the message to the server:
void sendCommand(String command) throws IOException {
String ipaddress = "192.168.0.2";
Socket commandSocket = null;
PrintWriter out = null;
BufferedWriter out = null;
BufferedReader in = null;
BufferedWriter outToDetailFile = null;
FileWriter fstream = null;
String version = "";
int numberOfBallsInGame;
int ledCycleState = 1;
commandSocket = new Socket(ipaddress, 7420);
out = new BufferedWriter(new OutputStreamWriter(commandSocket.getOutputStream()));
in = new BufferedReader(new InputStreamReader(commandSocket.getInputStream()));
out.write("c");out.flush();
out.write(command);out.flush();
String message = in.readLine();
//System.out.println(message);
out.close();
in.close();
commandSocket.close();
}
Now, because the server application is on the machine which does not accept more than 2 connections in 20 seconds i need to modify my method and "split" it in 3 different methods (i think).
My plan is the following:
I would like to call the connection to the server in one thread, keep it opened untill i want to close it, but i should be able to send the commands between opening the connection and closing it.
I'm pretty new to Java and i'll try to explain here exactly what i want to do:
1) I want to open the connection to TCP server.
2) After opening the connection i want to be able to send commands to an already opened connection by calling this method:
void sendCommand(String command) throws IOException {
out.write("c");out.flush();
out.write(command);out.flush();
}
And after i'm finished with sending commands i want to call some method to close my running connection.
Because i'm pretty new to java it would be very nice if someone could show me how to achieve this or modify my method.
Thank you in advance,
I'd like to map a port number to a user (linux user that is running a process that is binding to the port).
How can I do it in java?
I know I can go out to the shell and run bash commands that map a port to a PID, and then PID to user, but I'd like to keep it inside java if I can.
The more general question is: I have a webapp application that receives requests from localhost, and I'd like to know which local user performed the HttpServletRequest, so I can attach proper authorities to it.
Background:
I'm using spring security for all remote connections. However, I have a small part of the application (separated from the webapp) that is running locally alongside the application server, and that application is authenticated using the linux user mechanism. So for that reason, I bypass the server authentication rules for localhost (assuming all localhost access is permitted). The problem is with authorization - I need the identify the user running the localhost requests. Any idea how can I achieve this?
This is Linux dependent code, but not difficult to port to Windows.
This is not a Servlet code, but would work in that case as well:
Lets say I've a ServerSocket waiting on accept() call. When it receives a client request, it creates a Socket at another port to deal with that 'remote' request.
ServerSocket ss = new ServerSocket(2000);
System.out.println("Listening on local port : " + ss.getLocalPort());
while(...)
{
Socket s = ss.accept();
System.out.println("accepted client request, opened local port : " + s.getPort());
...
}
So, you need to feed the output of s.getPort() from above snippet to the following program's main() method.
public class FindUserByPort
{
public static void main(String[] args) throws Exception
{
String cmd = "netstat -anp | grep ";
int port = Integer.valueOf(args[0]);
cmd = cmd + port ;
Process pr = Runtime.getRuntime().exec(cmd);
InputStream is = pr.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = null;
List<Integer> pIDs = new ArrayList<Integer>();
while ((line = br.readLine()) != null)
{
if (line.contains("127.0.0.1:" + port))
{
String pidPname = line.substring(line.indexOf("ESTABLISHED") + "ESTABLISHED".length());
pidPname = pidPname.trim();
String pid = pidPname.split("/")[0];
pIDs.add(Integer.valueOf(pid));
}
}
if (pIDs.size() > 0)
{
for (int pid : pIDs)
{
String command = "top -n1 -b -p " + pid ;
Process p = Runtime.getRuntime().exec(command);
InputStream _is = p.getInputStream();
BufferedReader _br = new BufferedReader(new InputStreamReader(_is));
String _line = null;
while ((_line = _br.readLine()) != null)
{
_line = _line.trim();
if(_line.startsWith(String.valueOf(pid)))
{
String[] values = _line.split(" ");
System.out.println("pid : " + pid + ", user : " + values[1]);
}
}
_is.close();
_br.close();
}
}
is.close();
br.close();
}
}
I am trying to send an HTML POST request over telnet in Java, I have some XML content which I have to send. But when I try to achieve in java, i am getting "Connection Reset" error. But the same when I do it over putty(unix), I am getting the response xml correctly.
Java Program I used : (Resulting in Connection Reset error)
public class Telnet {public static void main(String[] args) throws Exception {
Socket socket = new Socket("hostname", 10020);
String xmled = "<?xml version=1.0?><methodCall><methodName>GetVoucherDetails</methodName><params><param><value><struct><member><name>serialNumber</name><value><string>1038291567</string></value></member><member><name>networkOperatorId</name><value><string>vno2</string></value></member></struct></value></param></params></methodCall>";
System.out.println("Params: " + xmled);
try {
Writer out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
out.write("POST /someContext HTTP/1.1\r\n");
out.write("Accept: text/xml\r\n");
out.write("Connection: close\r\n");
out.write("Content-Length: 489\r\n");
out.write("Content-Type: text/xml\r\n");
out.write("Host: ws2258:10010\r\n");
out.write("User-Agent: ADM/2.4/6.2\r\n");
out.write("Authorization: Basic cHBtc3VzZXI6dnNfJF9wcG11NWVy\r\n");
out.write(xmled);
out.write("\r\n");
out.flush();
InputStream inputstream = socket.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String string = null;
string = bufferedreader.readLine();
System.out.println(string);
while ((string = bufferedreader.readLine()) != null) {
System.out.println("Received " + string);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
socket.close();
}
}
}
Please suggest me something, I am new to socket programming.
In your Socket constructor, did you mean to put port 10020? HTTP implies port 80 unless your web server is listening on port 10020.
I finally have found the solution for this problem. The fix was quiet simple at the end. We had to send the entire XML content in one single line rather then putting into multiple lines.
so i made a little code that can download 4chan pages. i get the raw HTML page and parse it for my need. the code below was working fine but it suddenly stopped working. when i run it the server does not accept my request it seems its waiting for something more. however i know that HTTP request is as below
GET /ck HTTP/1.1
Host: boards.4chan.org
(extra new line)
if i change this format in anyway i revive "400 bad request" status code. but if i change HTTP/1.1 to 1.0 the server responses in "200 ok" status and i get the whole page. so this makes me thing the error is in the host line since that became mandatory in HTTP/1.1. but still i cannot figure out what exactly need to be changed.
the calling function simply this, to get one whole board
downloadHTMLThread( "ck", -1);
or for a specific thread u just change -1 to that number. for example like for the link below will have like below.
//http://boards.4chan.org/ck/res/3507158
//url.getDefaultPort() is 80
//url.getHost() is boards.4chan.org
//url.getFile() is /ck/res/3507158
downloadHTMLThread( "ck", 3507158);
any advise would be appreciated, thanks
public static final String BOARDS = "boards.4chan.org";
public static final String IMAGES = "images.4chan.org";
public static final String THUMBS = "thumbs.4chan.org";
public static final String RES = "/res/";
public static final String HTTP = "http://";
public static final String SLASH = "/";
public String downloadHTMLThread( String board, int thread) {
BufferedReader reader = null;
PrintWriter out = null;
Socket socket = null;
String str = null;
StringBuilder input = new StringBuilder();
try {
URL url = new URL(HTTP+BOARDS+SLASH+board+(thread==-1?SLASH:RES+thread));
socket = new Socket( url.getHost(), url.getDefaultPort());
reader = new BufferedReader( new InputStreamReader( socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
out.println( "GET " +url.getFile()+ " HTTP/1.1");
out.println( "HOST: " + url.getHost());
out.println();
long start = System.currentTimeMillis();
while ((str = reader.readLine()) != null) {
input.append( str).append("\r\n");
}
long end = System.currentTimeMillis();
System.out.println( input);
System.out.println( "\nTime: " +(end-start)+ " milliseconds");
} catch (Exception ex) {
ex.printStackTrace();
input = null;
} finally {
if( reader!=null){
try {
reader.close();
} catch (IOException ioe) {
// nothing to see here
}
}
if( socket!=null){
try {
socket.close();
} catch (IOException ioe) {
// nothing to see here
}
}
if( out!=null){
out.close();
}
}
return input==null? null: input.toString();
}
Try using Apache HttpClient instead of rolling your own:
static String getUriContentsAsString(String uri) throws IOException {
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(new HttpGet(uri));
return EntityUtils.toString(response.getEntity());
}
If you are doing this to really learn the internals of HTTP client requests, then you might start by playing with curl from the command line. This will let you get all your headers and request body squared away. Then it will be a simple matter of adjusting your request to match what works in curl.
By the code I think that you are sending 'HOST' instead of 'Host'. Since this is a compulsory header in http/1.1, but ignored in http/1.0, that might be the problem.
Anyway, you could use a program to capture the packet sent (i. e. wireshark), just to make sure.
Using println is quite useful, but the line separator appended to the command depends on the system property line.separator. I think (although I'm not sure) that the line separator used in http protocol has to be '\r\n'. If you're capturing the packet, I think it'd be a good idea to check that each line sent ends with '\r\n' (bytes x0D0A) (just in case your os line separator is different)
Use www.4chan.org as the host instead. Since boards.4chan.org is a 302 redirect to www.4chan.org, you won't be able to scrape anything from boards.4chan.org.
I am writing simple, unsophisticated web-server code in java. It seems to be finished, but I'm not quite sure how to test it. Could someone point me in the right direction? All the coding is finished, I just need to test the code. I tried running it from the terminal, and then connecting to localhost with a specified port, but I only get 404 NOT FOUNDs. I reiterate, I don't think this is a problem with the code, but with my guessing at methods by which to test drive said code. Ideas?
import java.io.*;
import java.net.*;
import java.util.*;
final class HttpRequest implements Runnable {
final static String CRLF = "\r\n";
Socket socket;
// Constructor
public HttpRequest(Socket socket) throws Exception {
this.socket = socket;
}
// Implement the run() method of the Runnable interface.
public void run() {
try {
processRequest();
} catch (Exception e) {
System.out.println(e);
}
}
private static void sendBytes(FileInputStream fis, OutputStream os)
throws Exception {
// Construct a 1K buffer to hold bytes on their way to the socket.
byte[] buffer = new byte[1024];
int bytes = 0;
// Copy requested file into the socket's output stream.
while((bytes = fis.read(buffer)) != -1 ) {
os.write(buffer, 0, bytes);
}
}
private static String contentType(String fileName) {
if(fileName.endsWith(".htm") || fileName.endsWith(".html")) {
return "text/html";
}
if(fileName.endsWith(".jpeg") || fileName.endsWith(".jpg")) {
return "image/jpeg";
}
if(fileName.endsWith(".gif")) {
return "image/gif";
}
return "application/octet-stream";
}
private void processRequest() throws Exception {
// Get a reference to the socket's input and output streams.
InputStream is = socket.getInputStream();
DataOutputStream os = new DataOutputStream(socket.getOutputStream());
// Set up input stream filters.
BufferedReader br = new BufferedReader(new InputStreamReader(is));
// Get the request line of the HTTP request message.
String requestLine = new String(br.readLine());
// Display the request line.
System.out.println();
System.out.println(requestLine);
// Get and display the header lines.
String headerLine = null;
while ((headerLine = br.readLine()).length() != 0) {
System.out.println(headerLine);
}
// Extract the filename from the request line.
StringTokenizer tokens = new StringTokenizer(requestLine);
tokens.nextToken(); // skip over the method, which should be "GET"
String fileName = tokens.nextToken();
// Prepend a "." so that file request is within the current directory.
fileName = "." + fileName;
// Open the requested file.
FileInputStream fis = null;
boolean fileExists = true;
try {
fis = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
fileExists = false;
}
// Construct the response message.
String statusLine = null;
String contentTypeLine = null;
String entityBody = null;
if (fileExists) {
statusLine = "200 OK" + CRLF;
contentTypeLine = "Content-type: " +
contentType( fileName ) + CRLF;
} else {
statusLine = "404 NOT FOUND" + CRLF;
contentTypeLine = "Content Not Found!" + CRLF;
entityBody = "<HTML>" +
"<HEAD><TITLE>Not Found</TITLE></HEAD>" +
"<BODY>Not Found</BODY></HTML>";
}
// Send the status line.
os.writeBytes(statusLine);
// Send the content type line.
os.writeBytes(contentTypeLine);
// Send a blank line to indicate the end of the header lines.
os.writeBytes(CRLF);
// Send the entity body.
if (fileExists) {
sendBytes(fis, os);
fis.close();
} else {
os.writeBytes("File DNE: Content Not Found!");
}
// Close streams and socket.
os.close();
br.close();
socket.close();
}
public static void main(String[] args) throws Exception {
final ServerSocket ss = new ServerSocket(8080);
while (true)
new HttpRequest(ss.accept()).run();
}
}
Solution:
SOLVED. It turns out, to test it, all you need to do is:
1) run the program from the terminal as per usual,
2) place the file you want to try to retrieve (lets say "example.html") into the same folder as your .java file(s),
3) in a separate terminal, run the command $ wget localhost:PORT/FILE.EXTENSION
(I used port 8080 here, so $ wget localhost:8080/example.html)
You should now see, in the folder you are currently sending the wget command from, an html response file "200 OK" or "404 File Not Server", along with the contents of the file if the former is true.
I was over-complicating this, as were the comments/replies... But it's done.
Guessing and checking ftw.
SOLVED. It turns out, to test it, all you need to do is:
1) run the program from the terminal as per usual,
2) place the file you want to try to retrieve (lets say "example.html") into the same folder as your .java file(s),
3) in a separate terminal, run the command $ wget localhost:PORT/FILE.EXTENSION
(I used port 8080 here, so $ wget localhost:8080/example.html)
You should now see, in the folder you are currently sending the wget command from, a response file "200 OK" or "404 File Not Server", along with the contents of the file if the former is true.
I was over-complicating this, as were the comments/replies... But it's done.
Guessing and checking ftw.