ソケットを使用して Java で単純な HTTP プロキシを作成しようとしています。基本的には、要求を受け取り、それを読み取ってサーバーに転送し、応答を読み取ってブラウザに戻します。私が抱えている問題は、解析する Content-Length フィールドがある応答にあり、ブラウザに送り返す正確なバイト数を読み取るループを書きましたが、ストリームの最後に到達する前に正しい量を読み取ります (読み取りは -1 を返します)。以下にプロキシの必要最小限のコードを追加します。接続が確立されると、メイン スレッドはこれらのクラスの 1 つを生成するだけです。誰にもアイデアはありますか?とても役に立ちます。
public class ProxyClient implements Runnable {
Socket clientSocket;
Socket netflixSocket;
InputStream isClient = null;
OutputStream osClient = null;
BufferedReader brClient = null;
InputStream isNetflix = null;
OutputStream osNetflix = null;
BufferedReader brNetflix = null;
boolean connectedNetflix = false;
String meta = ""; //header data
public ProxyClient(Socket connectionSocket){
this.clientSocket = connectionSocket;
try {
isClient = clientSocket.getInputStream();
osClient = clientSocket.getOutputStream();
brClient = new BufferedReader(new InputStreamReader(isClient));
} catch (IOException e1) {
e1.printStackTrace();
}
}
@Override
public void run() {
String host = "";
String temp; // temp read line from buffer
String getRequest;
int contentLength; //holder for reading bytes.
int bytesRead; // Total number of bytes read during consecutive reads
int numRead; // Number of bytes read in a single read.
byte[] dataBuffer = new byte[2000000];
while(true){
try {
// reading client HTTP request / connecting
host = "";
meta = "";
contentLength = 0;
getRequest = "";
while((temp = brClient.readLine()).length() > 0){
meta += temp + "\r\n";
//connect to server
if(!connectedNetflix && temp.startsWith("Host")){
// extract the host to connect to
host = temp.substring(temp.indexOf(':') + 2);
//connect to the host
try {
netflixSocket = new Socket(host, 80);
osNetflix = netflixSocket.getOutputStream();
isNetflix = netflixSocket.getInputStream();
brNetflix = new BufferedReader(new InputStreamReader(isNetflix));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
connectedNetflix = true;
}
else if (temp.startsWith("Content-Length:")) {
contentLength = Integer.parseInt(temp.substring(temp.indexOf(':') + 2));
}
}
meta += "\r\n"; // add blank line
//read request content, if any
if(contentLength > 0){
numRead = 0;
bytesRead = 0;
while(bytesRead != contentLength){
numRead = isNetflix.read(dataBuffer, bytesRead, contentLength-bytesRead);
if(numRead >0){
bytesRead += numRead;
}
}
}
//send to server
osNetflix.write(meta.getBytes());
if(contentLength > 0){
osClient.write(dataBuffer, 0, contentLength);
}
osNetflix.flush();
//Read response from server
contentLength = 0;
meta = "";
while((temp = brNetflix.readLine()).length() > 0){
meta += temp + "\r\n";
if (temp.startsWith("Content-Length:")) {
contentLength = Integer.parseInt(temp.substring(temp.indexOf(':') + 2));
}
}
meta += "\r\n"; // add blank line
//read Netflix content
if(contentLength > 0){
numRead = 0;
bytesRead = 0;
while(bytesRead != contentLength){
numRead = isNetflix.read(dataBuffer, bytesRead, contentLength-bytesRead);
if(numRead >0){
bytesRead += numRead;
}
}
}
// write back to browser/client
osClient.write(meta.getBytes());
if(contentLength > 0){
osClient.write(dataBuffer, 0, contentLength);
}
osClient.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
応答ヘッダーの例を次に示します。
HTTP/1.1 200 OK
Server: Apache
Accept-Ranges: bytes
Content-Type: text/plain
Content-Type: application/octet-stream
Access-Control-Allow-Origin: *
Content-Type: application/octet-stream
Last-Modified: Fri, 17 Jun 2011 07:52:37 GMT
ETag: "0d6a07a1c0e6772459e73f7c0c4fd899:1308297157"
Date: Thu, 31 May 2012 01:54:10 GMT
Content-Length: 294183
Connection: close
Cache-Control: no-store