/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.blackboxServer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import staticContent.framework.util.Util;
/**
* @author bash
*
*/
public class MixConnectionHandler implements Runnable {
// private ServerSocket exitConnectionSocket;
private Socket entryConnection;
private Socket exitConnection;
private long delayValue;
private DelayQueue<BufferStructure> entryToExitBuffer;
private DelayQueue<BufferStructure> exitToEntryBuffer;
private LinkedBlockingQueue<byte[]> en2Ex;
private LinkedBlockingQueue<byte[]> ex2En;
/**
* Constructor
* @param entryConnection
* @param delayValue
*/
public MixConnectionHandler(Socket entryConnection, long delayValue) {
this.entryConnection = entryConnection;
this.delayValue = delayValue;
openConnectioToMix();
// Buffer initialisieren
this.entryToExitBuffer = new DelayQueue<BufferStructure>();
this.exitToEntryBuffer = new DelayQueue<BufferStructure>();
en2Ex = new LinkedBlockingQueue<byte[]>();
ex2En = new LinkedBlockingQueue<byte[]>();
}
/**
* Connection to Exitclient
*/
private void openConnectioToMix() {
InetAddress address = null;
// System.out.println("mix: Try to connect to mixnetwork via localhost!");
try {
address = InetAddress.getByName("localhost");
} catch (UnknownHostException e) {
e.printStackTrace();
}
exitConnection = new Socket();
try {
exitConnection.setKeepAlive(true);
SocketAddress receiverAddress = new InetSocketAddress(address, 4003);
exitConnection.connect(receiverAddress);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// try {
// Thread.sleep(2000);
// } catch (InterruptedException e1) {
// continue;
// }
}
// System.out.println("client: multiplexed tunnel to mix (" + address + ":" + 4051 + ") established!");
}
@Override
public void run() {
try {
InputStreamService entryToExitInputStream = new InputStreamService(entryConnection.getInputStream(),
entryToExitBuffer, "entryToExit",en2Ex);
InputStreamService exitToEntryInputStream = new InputStreamService(exitConnection.getInputStream(),
exitToEntryBuffer, "exitToEntry", ex2En);
Thread entryToExitIn = new Thread(entryToExitInputStream);
Thread exitToEntryIn = new Thread(exitToEntryInputStream);
entryToExitIn.start();
exitToEntryIn.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
OutputStreamService entryToExitOutputStream = new OutputStreamService(exitConnection.getOutputStream(),
entryToExitBuffer, "entryToExit", en2Ex);
OutputStreamService exitToEntryOutputStream = new OutputStreamService(entryConnection.getOutputStream(),
exitToEntryBuffer, "exitToEntry", ex2En);
Thread entryToExitOut = new Thread(entryToExitOutputStream);
Thread exitToEntryOut = new Thread(exitToEntryOutputStream);
entryToExitOut.start();
exitToEntryOut.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Structure for Buffer
*
* @author bash
*/
private class BufferStructure implements Delayed {
public long timestamp;
public long expireTime;
private byte[] data;
public BufferStructure(byte[] data) {
this.timestamp = new java.util.Date().getTime();
this.data = data;
this.expireTime = timestamp +delayValue;
}
public long getTimestamp() {
return timestamp;
}
public byte[] getData() {
return data;
}
public void setData(byte[] data) {
this.data = data;
}
@Override
public long getDelay(TimeUnit timeUnit) {
// long remainingDelay = timestamp + delayValue - new java.util.Date().getTime();
long remainingDelay = expireTime - new java.util.Date().getTime();
// System.out.println(new String(data) + " "+ remainingDelay);
return timeUnit.convert(remainingDelay, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed arg0) {
return this.timestamp > ((BufferStructure) arg0).timestamp ? 1
: this.timestamp < ((BufferStructure) arg0).timestamp ? -1 : 0;
}
}
/**
* Subclass for inputStream
*
* @author bash
*/
private class InputStreamService implements Runnable {
private InputStream inputStream;
private DelayQueue<BufferStructure> buffer;
private LinkedBlockingQueue<byte[]> testQ;
public InputStreamService(InputStream inputStream, DelayQueue<BufferStructure> buffer, String side, LinkedBlockingQueue<byte[]> testQ) {
this.inputStream = inputStream;
this.buffer = buffer;
this.testQ = testQ;
}
@Override
public void run() {
byte[] incomingData;
while (true) {
int id;
int len;
byte[] message;
try {
id = Util.forceReadInt(inputStream);
len = Util.forceReadInt(inputStream);
message = Util.forceRead(inputStream, len);
incomingData = Util.concatArrays(Util.intToByteArray(id),
Util.concatArrays(Util.intToByteArray(len), message));
buffer.add(new BufferStructure(incomingData));
testQ.add(incomingData);
} catch (IOException e) {
System.out.println("mix: entry closed connection!");
break;
}
}
}
}
/**
* Subclass for outputstream
*
* @author bash
*
*/
private class OutputStreamService implements Runnable {
private OutputStream outputStream;
private DelayQueue<BufferStructure> buffer;
private LinkedBlockingQueue<byte[]> testQ;
public OutputStreamService(OutputStream outputStream, DelayQueue<BufferStructure> buffer, String side,LinkedBlockingQueue<byte[]> testQ) {
this.outputStream = outputStream;
this.buffer = buffer;
this.testQ = testQ;
}
@Override
public void run() {
while (true) {
try {
BufferStructure bufferElement = buffer.take();
byte[] data = bufferElement.getData();
byte[] data2 = testQ.take();
if(!Arrays.equals(data, data2)) {
// trafficlog.writeLine(new String(data));
// trafficlog.writeLine(new String(data2));
// trafficlog.writeLine(buffer.size() + " " + testQ.size());
// long timeNext = buffer.take().getTimestamp();
// trafficlog.writeLine(Long.toString(timeNext - oldStamp));
outputStream.write(data2);
} else {
// trafficlog.writeLine(new String(data));
outputStream.write(data);
}
outputStream.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}