I am trying to make a proxy server in java and I was able to make a working proxy which handles http requests properly. After searching a lot I was also able to extend my program for https requests by following this answer to a similar question: https://stackoverflow.com/a/9389125/5309299
Here's my code after a TCP connection is established between client and proxy:
String request = "";
byte[] requestByteArr;
//read the complete request
while(true){
String requestLine = bufferedReaderFromClient.readLine() + "\r\n";
if (requestLine.trim().length()==0 && !request.equals("")){
request+=requestLine;
requestByteArr = request.getBytes();
System.out.println(request);
break;
} else {
request+=requestLine;
}
}
String hostname = getHostFromRequest(request);
int remoteport = getRemotePortFromRequest(request);
if (request.startsWith("CONNECT")){
//establish connection between host and proxy
final Socket hostSocket = new Socket(hostname, remoteport);
//tell client that connection was successful
String statusLine = "HTTP/1.1 200 Connection established \n" + "Proxy-agent: ProxyServer/1.0\n" + "\r\n";
outToClient.write(statusLine.getBytes());
outToClient.flush();
//new thread to handle incoming responses from host
new Thread(){
public void run(){
try{
InputStream inFromHost = hostSocket.getInputStream();
while(true){
byte[] bufread = new byte[128];
int bytes_received;
while ((bytes_received = inFromHost.read(bufread)) > 0){
outToClient.write(bufread, 0, bytes_received);
outToClient.flush();
}
}
} catch (IOException e){
e.printStackTrace();
}
}
}.start();
//main thread handles incoming requests from client
OutputStream outToHost = hostSocket.getOutputStream();
while (true){
byte[] bufread = new byte[128];
int bytes_received;
while ((bytes_received = inFromClient.read(bufread)) > 0){
outToHost.write(bufread, 0, bytes_received);
outToHost.flush();
}
}
}
Obviously, this only works for one host, i.e. when a client (e.g. chrome browser) sends a CONNECT request for one host (e.g. "www.google.com:443"). I want my client to be able to connect with multiple hosts. The problem is that since all the requests that come after CONNECT request are encrypted, my proxy server will not be able to determine which request is meant for which host, so it cannot forward the requests.
Related
I want to send messages from my server to the client, and thats it. No handshake, or anything, server sends and client receives and thats it.
I have the backend in Java, and my front end is in angular.
The following method builds the socket, and the host and port etc and sends a message.
Scanner input = new Scanner(System.in);
while(true)
{
try
{
String host = "localhost";
int port = REDACTED;
InetAddress address = InetAddress.getByName(host);
Socket socket = new Socket(address, port);
//System.out.println("You're now connected to the Server"); /*this should only print once */
//Send the message to the server
OutputStream os = socket.getOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
String number;
number=input.next();
String sendMessage = number + "\n";
bw.write(sendMessage);
bw.flush();
System.out.println("Message sent to the server : "+sendMessage);
}
catch (IOException exception)
{
//System.out.println("Server is still offline");/*This should only print once*/
}
The issue is my Angular. I am not a web dev, and have no idea where my listener should be. I keep searching, but come across gradles/and pom.xml files etc, but I just need a simple listener on angular. I think it should be in index.html please correct me if im wrong. I basically need a simple angular listener that stores the message in a variable, thats it.
I am making a chat program. The problem occurs during the connection phase. Theoretically, the connection happens this way:
[Server] accept() incoming connection
[Client] attempt connection to the server
[Server & Client] initialize IO
[Client] send the username with a suffix indicating if there is or not a password following
[Server] receive username, check if there is an incoming password (if there is, read it too)
[Server] Check in database if client is already registered
[Server] ... (multiple checks of the client's account, etc...)
[Server] send an answer to the client if he is accepted or not
[Client] receive the answer and interpret it
Note that steps 6 and 7 are not yet implemented and the server skips to directly creating a new client account and accepting it in the network.
Now, something happens at either step 4 (if the error comes from the client not sending it's name) or at step 5 (if the error comes from the server not receiving the name), in either case, the server gets stuck waiting for the client's name and the client gets stuck waiting for the server's answer (step 9). I am asking your help in finding the cause of this bug and how to fix it.
Here is the code of the function used by the server to accept new connections:
public void establishConnection() {
Socket client = null;
BufferedReader input = null;
PrintWriter output = null;
String name = null;
String password = null;
try {
client = server.accept();
System.out.println("A connection is being established");
input = new BufferedReader(new InputStreamReader(client.getInputStream()));
output = new PrintWriter(client.getOutputStream());
//LOCATION OF THE BUG
//DESCRIPTION: If the name has been sent, the server seems not to receive it.
System.out.println("Waiting for the client's name...");
name = input.readLine();
if (name.charAt(name.length() - 1) == Keywords.PASSWORD) {
System.out.println("Waiting for the client's password...");
password = input.readLine();
} else
System.out.println("The client has no password");
name = name.substring(0, name.length() - 1);
System.out.println("Creation of the metaClient");
MetaClient metaClient = new MetaClient(name);
if (password != null)
metaClient.setPassword(password);
metaClient.setSocket(client);
System.out.println("Activating listening thread");
metaClient.activateCommunications(this, this);
/* If there is no already registered clients,
* the first to connect shall be the owner.
*/
if (clientsList.size() == 0)
metaClient.addRight(MetaClient.OWNER);
clientsList.add(metaClient);
output.write(Keywords.CONNECTION_ACCEPTED);
System.out.println("The connection process of user " + metaClient.getName()
+ " is complete.");
} catch (IOException e) {
System.out.println("Error when establishing a new connection");
e.printStackTrace();
} finally {
try {
if (input != null)
input.close();
if (output != null)
output.close();
} catch (IOException e) {
System.out.println("Error when closing the IO of the new connection.");
e.printStackTrace();
}
}
}
Here is the code of the builder of the client that tries to connect to the server:
public ChatClient(String ip, int port, String name, String password) throws ConnectException {
this.name = name;
boolean connectionRefused = false;
try {
System.out.println("Establishing connection to the server...");
server = new Socket(ip, port);
BufferedReader input = new BufferedReader(new InputStreamReader(server.getInputStream()));
PrintWriter output = new PrintWriter(server.getOutputStream());
//LOCATION OF THE BUG:
//BUG DESCRIPTION: The name seems not to be sent to the server
System.out.println("Sending name to the server...");
if (password == null || password == "") {
name += Keywords.NO_PASSWORD;
output.println(name);
} else {
name += Keywords.PASSWORD;
output.println(name);
System.out.println("Sending password to the server...");
output.println(password);
}
System.out.println("Waiting for the server's response...");
//Wait for the server's response
String response = input.readLine();
if (response.equals(Keywords.CONNECTION_ACCEPTED))
System.out.println("The connection has been accepted");
else
connectionRefused = true;
} catch (IOException e) {
System.out.println("Error when connecting to the server");
e.printStackTrace();
System.exit(1);
}
if (connectionRefused)
throw new ConnectException("The connection was refused by the server");
else {
communication = new CommunicationProtocol(server, this, this);
communication.start();
}
}
Any help would be really much appreciated, it makes quite some time now that i'm trying to fix this to no avail.
Edit to answer Scary Wombat's comment:
Yes, one machine runs the server and another runs the client. But the error happens even when both are running as two separate programs on one machine.
I am trying to build a proxy server and recently I am working on https. As specified in this post. I've tried to tunnel Connect request. My Code is as:
private boolean handleConnect(HttpServletRequest req,HttpServletResponse response){
String uri=req.getRequestURI();
String port="";
String host="";
int c=uri.indexOf(":");
if (c >= 0){
port = uri.substring(c + 1);
host = uri.substring(0,c);
if (host.indexOf('/') > 0)
host = host.substring(host.indexOf('/') + 1);
}
// Make Asyncronous connection
try{
InetSocketAddress inetAddress = new InetSocketAddress(host,Integer.parseInt(port));
{
InputStream in=req.getInputStream();
OutputStream out=response.getOutputStream();
if(true){
Socket sock=new Socket(host,Integer.parseInt(port));
IO.copy(in, sock.getOutputStream());
IO.copy(sock.getInputStream(), out);
if(!sock.getKeepAlive()){
sock.close();
}
}
}
}
catch(Exception ex){
ex.printStackTrace();
return false;
}
return true;
}
The code results java.net.UnknownHostException: google.com.np for https://google.com.np and Timeouts for https://Facebook.com . Why is that ??
Please suggest best way to tunnel Connect HTTP request.
Your UnknownHostException is due either to a non-existent host or a misconfigured DNS, and your connect timeout to a network connectivity problem, neither of which are on-topic here, but you can't really write a proper proxy this way. You need to start two threads per connection, one to copy bytes in each direction.
I got to implement a chat in my application. Connection to a server is made using sockets. I should register to that server and the server will aknowledge that with a reply.
I have implemented this in a single method where I send the command using a BufferedWriter, and then start reading from the input stream until it tells me there is no more data.
I read properly the server reply. However, I never get the negative value from the second in.read call and thus my method stays blocked in the while loop (in the conditionnal statement where I make that call).
How should this be done with sockets? I usually do that with files or other input streams without problem.
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
or make the server send a code to notify it has finished to send its response?
Currently I am doing the following:
private String sendSocketRequest(String request, boolean skipResponse) throws ChatException {
if (!isConnected()) openConnection();
try {
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()), 2048);
out.append(request);
out.flush();
out = null;
} catch (IOException e) {
LogHelper.error("Unable to send socket request: " + request, e);
throw new ChatException("Unable to send socket request: " + request, e);
}
try {
BufferedReader in = new BufferedReader(new InputStreamReader(
socket.getInputStream()), 2048);
StringBuffer response = new StringBuffer();
char[] buffer = new char[2048];
int charsRead = -1;
// >>>>>>>> This is where it gets blocked <<<<<<<<<
while ((charsRead = in.read(buffer)) >= 0) {
if (charsRead > 0) response.append(new String(buffer, 0, charsRead));
}
return response.toString();
} catch (IOException e) {
LogHelper.error("Unable to read socket response: " + request, e);
throw new ChatException("Unable to read socket response: " + request, e);
}
}
Connection to the server is made with the following method:
public synchronized void openConnection() throws ChatException {
try {
socket = new Socket(Constants.API_CHAT_SERVER_ADDRESS, Constants.API_CHAT_SERVER_PORT);
socket.setKeepAlive(true);
LogHelper.debug("CHAT >> Connected to the chat server: " + Constants.API_CHAT_SERVER_ADDRESS);
} catch (UnknownHostException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
} catch (IOException e) {
LogHelper.error("Unable to open chat connection", e);
throw new ChatException("Unable to open chat connection", e);
}
}
The amount of data to be sent/received over a socket based connection is protocol dependend and not known to the TCP/IP stack, but only to the application layer.
The protocol used is developer dependend ... ;-) so coming to your questions:
If I should read only the bytes I am supposed to read, does that mean that I either have to:
Know in advance the length of the server response?
Yes, this is one possibility.
or make the server send a code to notify it has finished to send its response?
Also yes, as this is another possibility. Common markers are \n or \r\n. The NUL/'\0' character also might make sense.
A third option is to prefix each data chunk with a constant number of bytes describing the amount of bytes to come.
Instead of dealing with bytes, maybe it's simpler handling instances of ad-hoc classes, like - for instance - a Message class:
The server:
// Streams
protected ObjectInputStream fromBuffer = null;
protected ObjectOutputStream toBuffer = null;
// Listening for a new connection
ServerSocket serverConn = new ServerSocket(TCP_PORT);
socket = serverConn.accept();
toBuffer = new ObjectOutputStream(socket.getOutputStream());
fromBuffer = new ObjectInputStream(socket.getInputStream());
// Receiving a new Message object
Message data = (Message)fromBuffer.readObject();
The client then sends a message by simply:
// Sending a message
Message data = new Message("Hello");
toBuffer.writeObject(data);
Message can be as complex as needed as long as its members implement Serializable interface.
As an assignment, I am allowed to use ServerSocket and Socket class only. Also it should be single-threaded as well.
I'm implementing a HTTP proxy server in Java, first it fetches request from client and then pushes to server, and then pushes the response back to the client.
The problem
The problem is, I have successfully get the request, send it to the end-server and get the proper HTTP response. I also can do print out the response in console. But it got stuck when I send the response to clientServer.outputstream. Firefox (requested to use, HTTP 1.0, no keep-alive requested) seems to load forever and nothing shows, and no response Firefox received from my program as well.
What I inspect when debug
Everytime a page start to load (FF request), there are always 2 client sockets. First socket contains null request, and second socket contains proper request. What I expect was that only one proper HTTP request from Firefox. Is that a weird behavior?
example:
/0:0:0:0:0:0:0:1:65194
[null request]
/0:0:0:0:0:0:0:1:65195
GET http://www.microsoft.com/ HTTP/1.0
Host: www.microsoft.com
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Proxy-Connection: close
Cookie: viewkey=lightweight; WT_FPC=id=269eb0e7618962f93a81347585923074:lv=1349229942007:ss=1349229580158; WT_NVR_RU=0=technet|msdn:1=:2=; omniID=c736269c_f430_4e9b_a42a_23a0c965c60a; MUID=212A1766CFE761423CD014BDCBE76158&TUID=1; MC1=GUID=08600fba7f5c5f409e67980d8a027593&HASH=ba0f&LV=20129&V=4&LU=1347643534618; A=I&I=AxUFAAAAAADGBwAA8ezRtqBBHjk3++mP1Bwj9w!!&V=4&CS=119EQ5002j10100; msdn=L=en-US
Code
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(60000);
while (true) {
clientSocket = serverSocket.accept();
[...]
// Extract request, and push to end-server
// Fetch response from end-server to client, using flush() already
// Close all input, output
// Close all sockets
} catch {[...]}
Any help is welcomed, thank you!
Full code as requested, I use PrintWriter, but before that using Byte makes no difference (not care efficiency)
import java.io.*;
import java.net.*;
import java.util.*;
public class Proxy {
static String separator = System.getProperty("line.separator");
public static void main(String args[]) {
//int port = Integer.parseInt(args[0]);
start(60000);
}
public static void start(int port) {
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(port);
Socket clientSocket = null;
while (true) {
clientSocket = serverSocket.accept();
System.out.println(clientSocket.getRemoteSocketAddress() + "\n" + clientSocket.getLocalSocketAddress() + "\n" + clientSocket.getInetAddress());
BufferedReader inStreamFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inLine;
Vector<String> clientRequestHeader = new Vector<String>();
String rawRequest = "";
while ((inLine = inStreamFromClient.readLine()) != null) {
if (!inLine.isEmpty()) {
clientRequestHeader.add(inLine);
rawRequest = rawRequest.concat(inLine + separator);
} else break;
}
while ((inLine = inStreamFromClient.readLine()) != null)
rawRequest = rawRequest.concat(inLine + separator);
System.out.println(rawRequest);
if (!rawRequest.isEmpty()) {
handleRequest(clientSocket, clientRequestHeader, rawRequest);
} else {
//clientSocket.close();
// Not sure how to handle null request
}
}
} catch (Exception e) {e.printStackTrace();}
}
public static void handleRequest(Socket clientSocket, Vector<String> clientRequestHeader, String rawRequest) {
HTTPRequest request = new HTTPRequest(clientRequestHeader, rawRequest);
try {
//System.out.println(rawRequest);
// Send request to end-server
Socket endServerSocket = new Socket(request.getHost(), 80);
PrintWriter outStreamToEndServer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(endServerSocket.getOutputStream())));
BufferedReader stringReader = new BufferedReader(new StringReader(rawRequest));
String inLine;
while ((inLine = stringReader.readLine())!= null) {
outStreamToEndServer.println(inLine);
}
outStreamToEndServer.println();
outStreamToEndServer.flush();
// Read response header from end-server
String responseHeader = "";
BufferedReader inStreamFromEndServer = new BufferedReader(new InputStreamReader(endServerSocket.getInputStream()));
while (!(inLine = inStreamFromEndServer.readLine()).isEmpty()) {
responseHeader = responseHeader.concat(inLine + separator);
}
// Send response header to client
PrintWriter outStreamToClient = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())));
outStreamToClient.println(responseHeader);
outStreamToClient.flush();
// Send response body to client
String responseBody = "";
while ((inLine = inStreamFromEndServer.readLine()) != null) {
responseBody = responseBody.concat(inLine + separator);
}
outStreamToClient.println(responseBody);
outStreamToClient.flush();
endServerSocket.shutdownInput();
clientSocket.shutdownOutput();
clientSocket.close();
endServerSocket.close();
//endServerSocket = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
first you should not use PrintWriter to transfer the Data, because the HTTP protocol isn't a pure text protocol the body can contain some raw data like images.
Replace your response transfer code with the code below.
InputStream in = endServerSocket.getInputStream();
OutputStream out = clientSocket.getOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, bytesRead);
}
in.close();
out.close();
Second point, you add always as line break the
static String separator = System.getProperty("line.separator");
This is the System specific line seperator. HTTP defines for the HTTP header and for the http header and body separation the ctrl line break charaters, so change this.
static String separator = "\r\n";
With this changes you will get your response to your browser.
Last Point you should change your client request read code also, because it will not always work if you want POST some data. Sometimes this data will transfered as raw data, by example file uploads.
Good Luck