Java 代理隧道 HTTPS
Java Proxy Tunnelling HTTPS
我正在 Java 中构建代理服务器;服务器正确处理 HTTP 流量,但无法正确传输 HTTPS 流量。根据 IETF draft,进行以下过程:
1) 我成功地从浏览器收到了 CONNECT 消息,其中包含要连接到的纯文本主机。
2)我解析此消息以提取主机详细信息并成功建立到远程主机的连接。
3) 然后我将 HTTP/1.0 200 Connection established 消息发送回客户端,然后立即尝试使用以下代码从连接的任何一侧中继流量。
问题是在我 return 上面的 200 条消息而不是发送 HTTPS 数据之后,浏览器似乎进入了一个无限循环并继续向代理发送更多的 CONNECT 消息。这是我用来在客户端和主机之间中继数据的代码:
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();
}
}
}
**重要提示:**
由于隧道传输的实际数据是加密的,因此对代理是不透明的,因此代理必须随时准备好从任何一方 read/write 。为了在单个线程中促进此过程,我在两侧设置了一个相对较短的套接字超时(2 秒)并进入一个循环,该循环在每次迭代时交替读取和写入哪一侧,其中没有数据可用 SocketTimeoutException发生被捕获,读取的一侧在此时切换并且循环继续执行。这种旨在在单个线程中从两个套接字读取的策略是否会导致问题?
socketOutputStreamsArr[curWriteIndex].write(dataBuff);
应该是
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?
是的。您应该使用两个线程或非阻塞 NIO。否则你只会增加很多不需要的延迟。
我正在 Java 中构建代理服务器;服务器正确处理 HTTP 流量,但无法正确传输 HTTPS 流量。根据 IETF draft,进行以下过程:
1) 我成功地从浏览器收到了 CONNECT 消息,其中包含要连接到的纯文本主机。
2)我解析此消息以提取主机详细信息并成功建立到远程主机的连接。
3) 然后我将 HTTP/1.0 200 Connection established 消息发送回客户端,然后立即尝试使用以下代码从连接的任何一侧中继流量。
问题是在我 return 上面的 200 条消息而不是发送 HTTPS 数据之后,浏览器似乎进入了一个无限循环并继续向代理发送更多的 CONNECT 消息。这是我用来在客户端和主机之间中继数据的代码:
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();
}
}
}
**重要提示:** 由于隧道传输的实际数据是加密的,因此对代理是不透明的,因此代理必须随时准备好从任何一方 read/write 。为了在单个线程中促进此过程,我在两侧设置了一个相对较短的套接字超时(2 秒)并进入一个循环,该循环在每次迭代时交替读取和写入哪一侧,其中没有数据可用 SocketTimeoutException发生被捕获,读取的一侧在此时切换并且循环继续执行。这种旨在在单个线程中从两个套接字读取的策略是否会导致问题?
socketOutputStreamsArr[curWriteIndex].write(dataBuff);
应该是
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?
是的。您应该使用两个线程或非阻塞 NIO。否则你只会增加很多不需要的延迟。