Reputation: 1195
I'm in the process of building a Proxy Server in Java; the server handles HTTP traffic correctly but fails to correctly tunnel HTTPS traffic. As per the IETF draft, the following process is undertaken:
1) I successfully receive the CONNECT message from the browser that contains the HOST to connect to in plain text.
2)I parse this message to extract the host details and successfully establish a connection to the remote host.
3)I then send a HTTP/1.0 200 Connection established message back to the client and then immediately attempt to relay traffic from either side of the connection using the code below.
The problem is after I return the above 200 message instead of sending HTTPS data, the browser seems to enter into an infinite loop and keep sending further CONNECT messages to the proxy. Here is the code I use to relay the data between the client and the host:
public static void stageTunnelledConnection(Socket clientSocket,Socket targetHostSocket) throws IOException{
//set client socket read timeout to 2 seconds. The targethost connection will ALREADY have been
//set to this value at the time this method is called.
clientSocket.setSoTimeout(2000);
InputStream[] socketInputStreamsArr = new InputStream[]{clientSocket.getInputStream(),targetHostSocket.getInputStream()};
OutputStream[] socketOutputStreamsArr = new OutputStream[]{clientSocket.getOutputStream(),targetHostSocket.getOutputStream()};
//holds current socket index to read from, this will be switched between the two sockets
//at 0 and 1 indexes of the sockets array respectively.
int curReadIndex = 0;
//this will be set according to the "curReadIndex" value and will typically be
//the logical NOT of that value; that is, where curReadIndex equals 0 the curWriteIndex to will equal 1 and visa versa.
int curWriteIndex = 1;
while(true){
try{
//attempt to read from socket stream at current index and write
//to the socket at the alternate index.
byte[] dataBuff = new byte[2048];
int bytesRead = 0;
//we read into the dataBuff this operation will block for
//a max of 2 seconds should no data be available to read
while((bytesRead = socketInputStreamsArr[curReadIndex].read(dataBuff)) != -1){
//ByteArrayInputStream bais = new ByteArrayInputStream(dataBuff);
//BufferedReader br = new BufferedReader(new InputStreamReader(bais));
//System.out.println(br.readLine());
//write the buffer to the outputsteam at the index
//computed and stored to the "curWriteIndex" var above.
socketOutputStreamsArr[curWriteIndex].write(dataBuff);
socketOutputStreamsArr[curWriteIndex].flush();
//System.out.println("Bytes read=".concat(String.valueOf(dataBuff)));
//System.out.println("wroteBytes: "+bytesRead);
}
}
catch(SocketTimeoutException ste){
//we switch read/write index each time a read timeout error occurs. I.e
//were there is no further data to read from the socket at the currrent read index.
if(ste.getMessage().contains("Read")){
//System.out.println("Switching connection.");
curReadIndex = (curReadIndex == 0) ? 1 : 0;
curWriteIndex = (curReadIndex == 0) ? 1 : 0;
}
else{
//clientSocket.close();
//targetHostSocket.close();
ste.printStackTrace();
}
}
catch(SocketException ioe){
//if an input/output exception occurs we must close both sockets
clientSocket.close();
targetHostSocket.close();
ioe.printStackTrace();
}
}
}
**IMPORTANT: ** As the actual data being tunnelled is encrypted and thus opaque to the proxy, the proxy must be prepared to read/write from either side at any time. In order to facilitate this process in a single thread I set a relatively short Socket Timeout (2 seconds) on both sides and enter into a loop which alternates which side it reads from and writes to on each iteration, where no data is available a SocketTimeoutException occurs which is caught, the side to read from is switched at this point and the loop continues to execute. Could this strategy that aims to read from two sockets in a single thread be causing the problem?
Upvotes: 0
Views: 526
Reputation: 311054
socketOutputStreamsArr[curWriteIndex].write(dataBuff);
That should be
socketOutputStreamsArr[curWriteIndex].write(dataBuff, 0, bytesRead);
In order to facilitate this process in a single thread I set a relatively short Socket Timeout (2 seconds) on both sides and enter into a loop which alternates which side it reads from and writes to on each iteration, where no data is available a SocketTimeoutException occurs which is caught, the side to read from is switched at this point and the loop continues to execute. Could this strategy that aims to read from two sockets in a single thread be causing the problem?
Yes. You should use either two threads or non-blocking NIO. Otherwise you're just adding lots of unwanted latency.
Upvotes: 1