I have a queue on Rabbitmq and each time I run my code the queue on the rebbitmq website is empty. I think it's because each time the basicConsume fetches all of the elements at once.
I need help in a way to get an element one at a time.
this is the function to add to queue:
public static void addToQueueRabbit(String qname, String id) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
//If we wanted to connect to a node on a different machine we'd simply specify its hostname or IP address here
factory.setHost("localhost");
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
//Declaring a queue is idempotent - it will only be created if it doesn't exist already.
channel.queueDeclare(qname, false, false, false, null);
String message = id;
channel.basicPublish("", qname, null, message.getBytes());
System.out.println( "Added to queue: "+qname);
}
}
this code is how the queue build:
public class waitingQueueConsumer implements Runnable {
private ConnectionFactory factory;
private Connection connection;
private Channel channel;
public waitingQueueConsumer() throws TimeoutException, IOException {
factory = new ConnectionFactory();
factory.setHost("localhost");
connection = factory.newConnection();
channel = connection.createChannel();
//declare the queue from which we're going to consume
channel.queueDeclare("waitingQueue", false, false, false, null);
// System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
System.out.println("init waitingQueueConsumer DONE");
}
public synchronized void run() {
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
//spliited to ID:permit:token
String[] splitted = message.split(":");
int permit = Integer.parseInt(splitted[1]);
String token = splitted[2];
System.out.println("permit: "+permit);
permit--;
//update the permit
message = splitted[0]+":"+permit+":"+token;
//TODO send the api request.
String id = splitted[0];
//to change to production when ready
apiService APICall= new apiService(id,token);
Boolean isUrlExist = null;
boolean isUrlExistArray[]=null;
boolean isMotobiEmpty= false;
Boolean tokenExpired = false;
JSONObject jsonObject=null;
try {
jsonObject = APICall.send();
if (jsonObject.get("data") instanceof JSONArray) {
tokenExpired=true;
}
else {
isUrlExistArray = APICall.checkForCsvLink(jsonObject);
if(isUrlExistArray[0]== true && isUrlExistArray[1]==false){
String urlMotobi = APICall.returnMotobiUrl(jsonObject);
csvCompare csvUrl = new csvCompare(urlMotobi, "");
isMotobiEmpty= csvUrl.checkIfReportEmpty(urlMotobi);
}
isUrlExist = isUrlExistArray[0] && isUrlExistArray[1];
}
} catch(ParseException e){
e.printStackTrace();
} catch(InterruptedException e){
e.printStackTrace();
}
try {
if(tokenExpired == true){
buildMail mymail = new buildMail(jsonObject);
mymail.mailForTokenExpired(id, token);
}
else if (isMotobiEmpty == true){
queueUtils.addToQueueRabbit("comparingQueue", "equal&" + id);
}
else if (isUrlExist) {
//the report is ready lets take the urls
//url1&url2&id
String urls = "";
urls = APICall.returnUrls(jsonObject);
APICall.setMailInfo(jsonObject);
queueUtils.addToQueueRabbit("comparingQueue", urls + "&" + id);
} else if (permit > 0) {
System.out.println("waiting 30 secs");
queueUtils.addToQueueRabbit("waitingQueue", message);
sleep(30000);
} else {
//inc the reports we handled
TestServiceApplication.compareReportsCounter++;
int num = TestServiceApplication.compareReportsCounter;
buildMail mymail = new buildMail(jsonObject);
mymail.mailForUnreadyUrl(APICall, isUrlExistArray, id);
}
} catch (TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
try {
channel.basicConsume("waitingQueue", true, deliverCallback, consumerTag -> { });
} catch (IOException e) {
e.printStackTrace();
}
}
}
i would like for help in explanation how to get only the fist element from rabbitmq and if its possible at all.
Related
I am trying to find a bug is some RabbitMQ client code that was developed six or seven years ago. The code was modified to allow for delayed messages. It seems that connections are created to the RabbitMQ server and then never destroyed. Each exists in a separate thread so I end up with 1000's of threads. I am sure the problem is very obvious / simple - but I am having trouble seeing it. I have been looking at the exchangeDeclare method (the commented out version is from the original code which seemed to work), but I have been unable to find the default values for autoDelete and durable which are being set in the modified code. The method below in within a Spring service class. Any help, advice, guidance and pointing out huge obvious errors appreciated!
private void send(String routingKey, String message) throws Exception {
String exchange = applicationConfiguration.getAMQPExchange();
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-delayed-type", "fanout");
Map<String, Object> headers = new HashMap<String, Object>();
headers.put("x-delay", 10000); //delay in miliseconds i.e 10secs
AMQP.BasicProperties.Builder props = new AMQP.BasicProperties.Builder().headers(headers);
Connection connection = null;
Channel channel = null;
try {
connection = myConnection.getConnection();
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get connection.");
e.printStackTrace();
return;
}
try {
if (connection != null) {
log.debug(" [CORE: AMQP] Sending message with key {} : {}",routingKey, message);
channel = connection.createChannel();
// channel.exchangeDeclare(exchange, exchangeType);
channel.exchangeDeclare(exchange, "x-delayed-message", true, false, args);
// channel.basicPublish(exchange, routingKey, null, message.getBytes());
channel.basicPublish(exchange, routingKey, props.build(), message.getBytes());
}
else {
log.error("Total AMQP melt down. This should never happen!");
}
}
catch(Exception e) {
log.error("AMQP send method Exception. Unable to get send.");
e.printStackTrace();
}
finally {
channel.close();
}
}
This is the connection class
#Service
public class PersistentConnection {
private static final Logger log = LoggerFactory.getLogger(PersistentConnection.class);
private static Connection myConnection = null;
private Boolean blocked = false;
#Autowired ApplicationConfiguration applicationConfiguration;
#PreDestroy
private void destroy() {
try {
myConnection.close();
} catch (IOException e) {
log.error("Unable to close AMQP Connection.");
e.printStackTrace();
}
}
public Connection getConnection( ) {
if (myConnection == null) {
start();
}
return myConnection;
}
private void start() {
log.debug("Building AMQP Connection");
ConnectionFactory factory = new ConnectionFactory();
String ipAddress = applicationConfiguration.getAMQPHost();
String user = applicationConfiguration.getAMQPUser();
String password = applicationConfiguration.getAMQPPassword();
String virtualHost = applicationConfiguration.getAMQPVirtualHost();
String port = applicationConfiguration.getAMQPPort();
try {
factory.setUsername(user);
factory.setPassword(password);
factory.setVirtualHost(virtualHost);
factory.setPort(Integer.parseInt(port));
factory.setHost(ipAddress);
myConnection = factory.newConnection();
}
catch (Exception e) {
log.error("Unable to initialise AMQP Connection.");
e.printStackTrace();
}
myConnection.addBlockedListener(new BlockedListener() {
public void handleBlocked(String reason) throws IOException {
// Connection is now blocked
log.warn("Message Server has blocked. It may be resource limitted.");
blocked = true;
}
public void handleUnblocked() throws IOException {
// Connection is now unblocked
log.warn("Message server is unblocked.");
blocked = false;
}
});
}
public Boolean isBlocked() {
return blocked;
}
}
First some Information regarding my Setup.
I have a S8 Cellphone, where i run this App, based upon the AR-Devkit demo from Google.
public void closeSocket(DatagramSocket socket) {
if (socket != null && socket.isConnected() ) {
while (!socket.isConnected()) {
socket.disconnect();
try {
Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
} catch (InterruptedException e) {
Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
e.printStackTrace();
}
}
}
if (socket != null && !socket.isClosed()) {
socket.close();
while (!socket.isClosed()) {
try {
Thread.sleep(SpringAR.TIME_OUT_IN_BROADCAST);
} catch (InterruptedException e) {
Log.d(SpringAR.protocollDebugLogPrefix, " Socket Closing interrupted");
e.printStackTrace();
}
}
}
}
public DatagramSocket createSocket(InetAddress ipAddress, int port) {
try {
DatagramSocket socket = new DatagramSocket(null);
InetSocketAddress address = new InetSocketAddress(ipAddress, port);
socket.setReuseAddress(true);
socket.bind(address);
return socket;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public DatagramSocket getBroadcastListenerSocket() throws IOException {
InetSocketAddress anyAdress = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 9000);
DatagramSocket socket = new DatagramSocket(null);
socket.setSoTimeout(30);
socket.setReuseAddress(true);
socket.bind(anyAdress);
return socket;
}
public DatagramSocket getBroadcastSenderSocket(DatagramSocket oldSocket) {
DatagramSocket socket = null;
try {
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
socket = getSocket(oldSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
socket.setBroadcast(true);
socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
} catch (IOException e) {
e.printStackTrace();
}
return socket;
}
public DatagramSocket getSocket(DatagramSocket oldSocket, InetAddress ipAddress, int port, InetAddress targetAddress) {
if (oldSocket != null ) {
closeSocket(oldSocket);
}
DatagramSocket socket = null;
try {
socket = createSocket(ipAddress, port);
socket.setBroadcast(false);
socket.setSoTimeout(SpringAR.TIME_OF_FRAME_IN_MS);
if (targetAddress != null)
socket.connect(targetAddress, port);
} catch (SocketException e) {
e.printStackTrace();
}
return socket;
}
public class DatagramReciever extends Thread {
private String datagramToSend = "";
private boolean newDatagramToSend = false;
private DatagramPacket snd_packet;
DatagramSocket senderSocket = null;
DatagramSocket listenerSocket = null;
private DatagramSocket broadCastListenerSocket;
//Buffer gettters and setters
private int writeBuffer = 0;
private SpringAR.comStates oldState;
int getReadBuffer() {
if (writeBuffer == 1) return 0;
return 1;
}
void switchBuffer() {
recieveByteIndex = 0;
writeBuffer = getReadBuffer();
}
public String dbg_message = "";
//Management Communication Headers
public void kill() {
closeSocket(senderSocket);
closeSocket(listenerSocket);
closeSocket(broadCastListenerSocket);
}
public void run() {
try {
initializeBroadcastConnection();
while (true) {
//Recieving Datagramm
DatagramPacket rcv_packet = new DatagramPacket(rcv_message[writeBuffer], rcv_message[writeBuffer].length);
boolean NewMessageArrived = true;
try {
listenerSocket.receive(rcv_packet);
} catch (SocketTimeoutException e) {
NewMessageArrived = false;
}
//Watchdog
handleWatchDogTimer(State);
//TODO Delete String conversion
if (NewMessageArrived) {
dbg_message = new String(rcv_message[writeBuffer], 0, rcv_packet.getLength(), "US-ASCII");
Log.d(SpringAR.dataDebugLogPrefix, "" + rcv_packet.getAddress().getHostAddress() + ": " + dbg_message.trim() + " of " + rcv_packet.getLength() + "length ");
}
if (validatePackageSender(rcv_packet)) {
connectionStateMachine(rcv_message, rcv_packet);
}
//Sending Datagram
if (newDatagramToSend && hostIpAddress != null) {
//Log.d(SpringAR.protocollDebugLogPrefix, "Server sending: " + datagramToSend);
byte[] snd_message = datagramToSend.getBytes();
try {
snd_packet = packSendPackageByState(snd_message);
assert (snd_packet != null);
senderSocket.send(snd_packet);
newDatagramToSend = false;
} catch (IOException e1) {
e1.printStackTrace();
//causes Caused by: android.system.ErrnoException: sendto failed: EINVAL (Invalid argument)
Log.e(SpringAR.protocollDebugLogPrefix, "Server Error in State: " + State.name());
break;
}
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
private void initializeBroadcastConnection() throws IOException {
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
senderSocket = getSocket(null, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, null);
broadCastListenerSocket = getBroadcastListenerSocket();
listenerSocket = broadCastListenerSocket;
Log.d(SpringAR.protocollDebugLogPrefix, "initializeBroadcastConnection completed");
}
// handles management traffic like configurstion files
private void connectionStateMachine(byte[][] payload, DatagramPacket rcv_packet) throws IOException {
//Reset triggered by Host
if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveResetHeaderByte) != SpringAR.STRING_NOT_FOUND) {
State = SpringAR.comStates.STATE_resetCommunication;
}
Log.d(SpringAR.protocollDebugLogPrefix, "ConnectionStateMachine: " + State.name());
switch (State) {
case STATE_resetCommunication: {
messageCounter = 0;
listenerSocket = broadCastListenerSocket;
hostIpAddress = comonUtils.getBroadcastAddress(context);
senderSocket = getBroadcastSenderSocket(senderSocket);
setSendToSpringMessage(SpringAR.sendResetHeader);
State = SpringAR.comStates.STATE_broadCastHeader;
return;
}
case STATE_broadCastHeader: {
if (comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveHostReplyHeaderByte) != SpringAR.STRING_NOT_FOUND) {
Log.d(SpringAR.protocollDebugLogPrefix, " Host Reply Header recieved");
//Extract the hostIp
String hostIpAdressAsString = new String(payload[writeBuffer]);
hostIpAdressAsString = hostIpAdressAsString.replace(SpringAR.recieveHostReplyHeader, "").trim();
Log.d(SpringAR.dataDebugLogPrefix, hostIpAdressAsString);
hostIpAddress = InetAddress.getByName(hostIpAdressAsString);
//Set Connection from broadcast to target
ARDeviceAddress = InetAddress.getByName(comonUtils.getIPAddress(true));
Log.d(SpringAR.protocollDebugLogPrefix, " New Device Adress " + ARDeviceAddress);
senderSocket = getSocket(senderSocket, ARDeviceAddress, SpringAR.UDP_SERVER_PORT, hostIpAddress);
listenerSocket = senderSocket;
State = SpringAR.comStates.STATE_sendCFG;
return;
}
setSendToSpringMessage(SpringAR.sendBroadcasteHeader);
delayByMs(SpringAR.TIME_OUT_IN_BROADCAST);
return;
}
case STATE_sendCFG: {
if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveCFGHeaderByte )) {
State = SpringAR.comStates.STATE_sendRecieveData;
return;
}
setSendToSpringMessage(SpringAR.formConfigurationMessage());
return;
}
case STATE_sendRecieveData: {
if ( SpringAR.STRING_NOT_FOUND != comonUtils.indexOf(payload[writeBuffer], SpringAR.recieveDataHeaderByte)) {
writeRecievedDataToBuffer(payload[writeBuffer], rcv_packet.getLength());
}
break;
}
default:
Log.d(SpringAR.protocollDebugLogPrefix, "Connection State Machine invalid state");
}
}
https://github.com/PicassoCT/arcore-android-sdk/blob/6c9b48a3d520e039cd48bc2af7354ccdec857736/arcore-android-sdk/samples/hello_ar/app/src/main/java/com/google/ar/core/examples/app/common/tcpClient/Server.java
All the testing is happening in a home-WiFi Setup, where the desktop with the host-application is directly attached to the WiFi-Router.
What is working thus far:
The device can broadcast its presence.
The host can broadcast its configuration.
The Device can not communicate from IP to IP on the host. Both sides have fixed IP set.
I can communicate with the App PacketSender with the host-Application, and ruled a failure on its part out.
I also built a smaller debug-loop, to only send udp-packets back and forth, which also worked.
Thank you for your time
Change this line:
socket.setSoTimeout(30);
to
socket.setSoTimeout(1000);
You've got a fairly complex state machine going here, and it's difficult to discern what is happening without having logs to look at. I would summarize your state machine like this:
Broadcast a configuration message
Spend 30ms listening for a response
If no response is received, block for SpringAR.TIME_OF_FRAME_IN_MS (not included in your code; I assume it is 1000ms), and loop back to #1
If a response was received, send a reply directly to the peer, and go to #2
#4 is the step that isn't happening. The likely reason (based on the Wireshark dump) is that it's taking 68ms for ARDevice's response to reach "Host". You only gave it 30ms. There could be a number of reasons it's taking so long, but that's beyond the scope of your question.
Disclaimer- I am not a Java programmer. Odds are I'll need to do my homework on any advice given, but I will gladly do so :)
That said, I wrote a complete database-backed socket server which is working just fine for my small tests, and now I'm getting ready for initial release. Since I do not know Java/Netty/BoneCP well- I have no idea if I made a fundamental mistake somewhere that will hurt my server before it even gets out the door.
For example, I have no idea what an executor group does exactly and what number I should use. Whether it's okay to implement BoneCP as a singleton, is it really necessary to have all those try/catch's for each database query? etc.
I've tried to reduce my entire server to a basic example which operates the same way as the real thing (I stripped this all in text, did not test in java itself, so excuse any syntax errors due to that).
The basic idea is that clients can connect, exchange messages with the server, disconnect other clients, and stay connected indefinitely until they choose or are forced to disconnect. (the client will send ping messages every minute to keep the connection alive)
The only major difference, besides untesting this example, is how the clientID is set (safely assume it is truly unique per connected client) and that there is some more business logic in checking of values etc.
Bottom line- can anything be done to improve this so it can handle the most concurrent users possible? Thanks!
//MAIN
public class MainServer {
public static void main(String[] args) {
EdgeController edgeController = new EdgeController();
edgeController.connect();
}
}
//EdgeController
public class EdgeController {
public void connect() throws Exception {
ServerBootstrap b = new ServerBootstrap();
ChannelFuture f;
try {
b.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class)
.localAddress(9100)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new EdgeInitializer(new DefaultEventExecutorGroup(10)));
// Start the server.
f = b.bind().sync();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally { //Not quite sure how to get here yet... but no matter
// Shut down all event loops to terminate all threads.
b.shutdown();
}
}
}
//EdgeInitializer
public class EdgeInitializer extends ChannelInitializer<SocketChannel> {
private EventExecutorGroup executorGroup;
public EdgeInitializer(EventExecutorGroup _executorGroup) {
executorGroup = _executorGroup;
}
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("idleStateHandler", new IdleStateHandler(200,0,0));
pipeline.addLast("idleStateEventHandler", new EdgeIdleHandler());
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.nulDelimiter()));
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(this.executorGroup, "handler", new EdgeHandler());
}
}
//EdgeIdleHandler
public class EdgeIdleHandler extends ChannelHandlerAdapter {
private static final Logger logger = Logger.getLogger( EdgeIdleHandler.class.getName());
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
if(evt instanceof IdleStateEvent) {
ctx.close();
}
}
private void trace(String msg) {
logger.log(Level.INFO, msg);
}
}
//DBController
public enum DBController {
INSTANCE;
private BoneCP connectionPool = null;
private BoneCPConfig connectionPoolConfig = null;
public boolean setupPool() {
boolean ret = true;
try {
Class.forName("com.mysql.jdbc.Driver");
connectionPoolConfig = new BoneCPConfig();
connectionPoolConfig.setJdbcUrl("jdbc:mysql://" + DB_HOST + ":" + DB_PORT + "/" + DB_NAME);
connectionPoolConfig.setUsername(DB_USER);
connectionPoolConfig.setPassword(DB_PASS);
try {
connectionPool = new BoneCP(connectionPoolConfig);
} catch(SQLException ex) {
ret = false;
}
} catch(ClassNotFoundException ex) {
ret = false;
}
return(ret);
}
public Connection getConnection() {
Connection ret;
try {
ret = connectionPool.getConnection();
} catch(SQLException ex) {
ret = null;
}
return(ret);
}
}
//EdgeHandler
public class EdgeHandler extends ChannelInboundMessageHandlerAdapter<String> {
private final Charset CHARSET_UTF8 = Charset.forName("UTF-8");
private long clientID;
static final ChannelGroup channels = new DefaultChannelGroup();
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Connection dbConnection = null;
Statement statement = null;
ResultSet resultSet = null;
String query;
Boolean okToPlay = false;
//Check if status for ID #1 is true
try {
query = "SELECT `Status` FROM `ServerTable` WHERE `ID` = 1";
dbConnection = DBController.INSTANCE.getConnection();
statement = dbConnection.createStatement();
resultSet = statement.executeQuery(query);
if (resultSet.first()) {
if (resultSet.getInt("Status") > 0) {
okToPlay = true;
}
}
} catch (SQLException ex) {
okToPlay = false;
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException logOrIgnore) {
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException logOrIgnore) {
}
}
if (dbConnection != null) {
try {
dbConnection.close();
} catch (SQLException logOrIgnore) {
}
}
}
if (okToPlay) {
//clientID = setClientID();
sendCommand(ctx, "HELLO", "WORLD");
} else {
sendErrorAndClose(ctx, "CLOSED");
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
channels.remove(ctx.channel());
}
#Override
public void messageReceived(ChannelHandlerContext ctx, String request) throws Exception {
// Generate and write a response.
String[] segments_whitespace;
String command, command_args;
if (request.length() > 0) {
segments_whitespace = request.split("\\s+");
if (segments_whitespace.length > 1) {
command = segments_whitespace[0];
command_args = segments_whitespace[1];
if (command.length() > 0 && command_args.length() > 0) {
switch (command) {
case "HOWDY": processHowdy(ctx, command_args); break;
default: break;
}
}
}
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
TraceUtils.severe("Unexpected exception from downstream - " + cause.toString());
ctx.close();
}
/* */
/* STATES - / CLIENT SETUP */
/* */
private void processHowdy(ChannelHandlerContext ctx, String howdyTo) {
Connection dbConnection = null;
Statement statement = null;
ResultSet resultSet = null;
String replyBack = null;
try {
dbConnection = DBController.INSTANCE.getConnection();
statement = dbConnection.createStatement();
resultSet = statement.executeQuery("SELECT `to` FROM `ServerTable` WHERE `To`='" + howdyTo + "'");
if (resultSet.first()) {
replyBack = "you!";
}
} catch (SQLException ex) {
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException logOrIgnore) {
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException logOrIgnore) {
}
}
if (dbConnection != null) {
try {
dbConnection.close();
} catch (SQLException logOrIgnore) {
}
}
}
if (replyBack != null) {
sendCommand(ctx, "HOWDY", replyBack);
} else {
sendErrorAndClose(ctx, "ERROR");
}
}
private boolean closePeer(ChannelHandlerContext ctx, long peerClientID) {
boolean success = false;
ChannelFuture future;
for (Channel c : channels) {
if (c != ctx.channel()) {
if (c.pipeline().get(EdgeHandler.class).receiveClose(c, peerClientID)) {
success = true;
break;
}
}
}
return (success);
}
public boolean receiveClose(Channel thisChannel, long remoteClientID) {
ChannelFuture future;
boolean didclose = false;
long thisClientID = (clientID == null ? 0 : clientID);
if (remoteClientID == thisClientID) {
future = thisChannel.write("CLOSED BY PEER" + '\n');
future.addListener(ChannelFutureListener.CLOSE);
didclose = true;
}
return (didclose);
}
private ChannelFuture sendCommand(ChannelHandlerContext ctx, String cmd, String outgoingCommandArgs) {
return (ctx.write(cmd + " " + outgoingCommandArgs + '\n'));
}
private ChannelFuture sendErrorAndClose(ChannelHandlerContext ctx, String error_args) {
ChannelFuture future = sendCommand(ctx, "ERROR", error_args);
future.addListener(ChannelFutureListener.CLOSE);
return (future);
}
}
When a network message arrive at server, it will be decoded and will release a messageReceived event.
If you look at your pipeline, last added thing to pipeline is executor. Because of that executor will receive what has been decoded and will release the messageReceived event.
Executors are processor of events, server will tell which events happening through them. So how executors are being used is an important subject. If there is only one executor and because of that, all clients using this same executor, there will be a queue for usage of this same executor.
When there are many executors, processing time of events will decrease, because there will not be any waiting for free executors.
In your code
new DefaultEventExecutorGroup(10)
means this ServerBootstrap will use only 10 executors at all its lifetime.
While initializing new channels, same executor group being used:
pipeline.addLast(this.executorGroup, "handler", new EdgeHandler());
So each new client channel will use same executor group (10 executor threads).
That is efficient and enough if 10 threads are able to process incoming events properly. But if we can see messages are being decoded/encoded but not handled as events quickly, that means there is need to increase amount of them.
We can increase number of executors from 10 to 100 like that:
new DefaultEventExecutorGroup(100)
So that will process event queue faster if there is enough CPU power.
What should not be done is creating new executor for each new channel:
pipeline.addLast(new DefaultEventExecutorGroup(10), "handler", new EdgeHandler());
Above line is creating a new executor group for each new channel, that will slow down things greatly, for example if there are 3000 clients, there will be 3000 executorgroups(threads). That is removing main advantage of NIO, ability to use with low thread amounts.
Instead of creating 1 executor for each channel, we can create 3000 executors at startup and at least they will not be deleted and created each time a client connects/disconnects.
.childHandler(new EdgeInitializer(new DefaultEventExecutorGroup(3000)));
Above line is more acceptable than creating 1 executor for each client, because all clients are connected to same ExecutorGroup and when a client disconnects Executors still there even if client data is removed.
If we must speak about database requests, some database queries can take long time to being completed, so if there are 10 executorss and there are 10 jobs being processed, 11th job will have to wait until one of others complete. This is a bottleneck if server receiving more than 10 very time consuming database job at the same time. Increasing count of executors will solve bottleneck to some degree.
I am new to Netty and trying to writing a server client project using it. I am able to send the request from client to the server successfully and am able to process it too using my listeners. But the issue am having is when I try to write the response back to the channel for the client processing on the server side using channel.write(), I am unable to get it back at client.
This is my first time on stackoverflow, please forgive if I make some mistake while asking the question or for indentation issues while posting the code.
This is my server :
public class SocketIOServer {
private Logger log = Logger.getLogger(getClass());
private ServerBootstrap bootstrap;
private Channel serverChannel;
private int port;
private boolean running;
public SocketIOServer(int port) {
this.port = port;
this.running = false;
}
public boolean isRunning() {
return this.running;
}
public void start() {
bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
try {
EncryptionManager encryptionManager = EncryptionManager.getInstance();
} catch (Exception ex) {
java.util.logging.Logger.getLogger(SocketIOServer.class.getName()).log(Level.SEVERE, null, ex);
}
// Set up the event pipeline factory.
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = pipeline();
pipeline.addLast("decode", new FrameDecoder(){
#Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
// Make sure if the length field was received.
if (buf.readableBytes() < 4) {
// The length field was not received yet - return null.
// This method will be invoked again when more packets are
// received and appended to the buffer.
return null;
}
// The length field is in the buffer.
// Mark the current buffer position before reading the length field
// because the whole frame might not be in the buffer yet.
// We will reset the buffer position to the marked position if
// there's not enough bytes in the buffer.
buf.markReaderIndex();
// Read the length field.
int length = buf.readInt();
// Make sure if there's enough bytes in the buffer.
if (buf.readableBytes() < length) {
// The whole bytes were not received yet - return null.
// This method will be invoked again when more packets are
// received and appended to the buffer.
// Reset to the marked position to read the length field again
// next time.
buf.resetReaderIndex();
return null;
}
// There's enough bytes in the buffer. Read it.
ChannelBuffer frame = buf.readBytes(length);
// Successfully decoded a frame. Return the decoded frame.
return frame;
}
});
pipeline.addLast("handler", new GameServerHandler());
return pipeline;
}
});
bootstrap.setOption("backlog", 1024);
// Bind and start to accept incoming connections.
this.serverChannel = bootstrap.bind(new InetSocketAddress(port));
this.running = true;
log.info("Server Started at port [" + port + "]");
System.out.println("Server Started at port [" + port + "]");
}
public static void main(String[] args) {
SocketIOServer server = new SocketIOServer(8888);
server.start();
}
Server Handler class :
public class GameServerHandler extends SimpleChannelUpstreamHandler {
#Override
public void handleUpstream(
ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
if (e instanceof ChannelStateEvent) {
logger.info(e.toString());
}
super.handleUpstream(ctx, e);
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception{
System.out.println("Following data recieved at the server");
ConnectionManager connectionManager = ConnectionManager.getInstance();
List<Object> paramsList = new ArrayList<Object>();
NetworkClient client = connectionManager.getNetworkClient(e.getChannel().getId());
ChannelBuffer ebuf = (ChannelBuffer)e.getMessage();
if( client == null ) {
client = new NetworkClient(e.getChannel());
connectionManager.addNetworkClient(e.getChannel().getId(), client);
}
byte [] encryptedData = ebuf.array();
System.out.println("encrypted data size : "+ encryptedData.length);
System.out.println("encrypted data : "+ encryptedData);
byte [] decrpytedData = null;
try {
decrpytedData = EncryptionManager.getInstance().decrypt(encryptedData, EncryptionManager.getInstance().getPrivateKey());
}catch (Throwable ee){
ee.printStackTrace();
}
ChannelBuffer buf = ChannelBuffers.buffer(decrpytedData.length);
buf.writeBytes(decrpytedData);
while(buf.readable()) {
long gameTableId = buf.readLong();
GameTable gameTable = gameTableController.getTablePeer(gameTableId);
if(gameTable == null) {
GameTable newGameTable = new GameTable();
newGameTable.setTableId(gameTableId);
newGameTable.registerListeners();
gameTableController.storeTablePeer(gameTableId, newGameTable);
}
int eventHash = buf.readInt();
String eventName = getEventNameFromEventHash(eventHash);
int paramCount = buf.readInt();
if(paramCount > 0) {
for(int count=0;count<paramCount;count++) {
populateParamList(buf, paramsList);
}
if(!NetworkMessenger.broadcastToAllFromNetwork(eventName, client, paramsList.toArray(new Object[paramsList.size()]))) {
logger.debug( "Unhandled Data:" + eventName);
System.out.println("Unhandled Data:" + eventName);
}
logger.debug( "Data processed successfully for " + eventName + " game table id : " + gameTableId);
System.out.println("Data processed successfully for " + eventName + " game table id : " + gameTableId);
}
break;
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
logger.log(
Level.WARN,
"Unexpected exception from downstream.",
e.getCause());
Channel ch = e.getChannel();
ch.close();
}
#Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
ChannelFuture cf = e.getFuture();
cf.addListener(new Greeter());
}
#Override
public void channelDisconnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Unregister the channel from the global channel list
// so the channel does not receive messages anymore.
channels.remove(e.getChannel());
}
private static final class Greeter implements ChannelFutureListener {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
// Once session is secured, send a greeting.
String welcomeMsg = "Welcome";
ChannelBuffer cb = ChannelBuffers.dynamicBuffer();
cb.writeBytes(welcomeMsg.getBytes());
future.getChannel().write(cb);
// Register the channel to the global channel list
// so the channel received the messages from others.
channels.add(future.getChannel());
} else {
future.getChannel().close();
}
}
}
}
This is my client class :
public class Clientbot {
public static void main(String[] args) throws IOException {
String host = "localhost";
int port = 8888;
final ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
// Configure the client.
ChannelFactory factory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ClientBootstrap bootstrap = new ClientBootstrap(factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decode", new FrameDecoder(){
#Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buf) throws Exception {
// Make sure if the length field was received.
if (buf.readableBytes() < 4) {
return null;
}
buf.markReaderIndex();
// Read the length field.
int length = buf.readInt();
// Make sure if there's enough bytes in the buffer.
if (buf.readableBytes() < length) {
buf.resetReaderIndex();
return null;
}
// There's enough bytes in the buffer. Read it.
ChannelBuffer frame = buf.readBytes(length);
// Successfully decoded a frame. Return the decoded frame.
return frame;
}
});
pipeline.addLast("handler", new ClientHandler());
return pipeline;
}
});
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("keepAlive", true);
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
// Wait until the connection attempt succeeds or fails.
Channel channel = future.awaitUninterruptibly().getChannel();
if (!future.isSuccess()) {
future.getCause().printStackTrace();
bootstrap.releaseExternalResources();
return;
}
ChannelFuture lastWriteFuture = null;
try {
lastWriteFuture = writeSecureSampleData(channel, buf);
} catch (Exception ex) {
ex.printStackTrace();
}
// Wait until all messages are flushed before closing the channel.
if (lastWriteFuture != null) {
lastWriteFuture.awaitUninterruptibly();
}
// Close the connection. Make sure the close operation ends because
// all I/O operations are asynchronous in Netty.
channel.close().awaitUninterruptibly();
// Shut down all thread pools to exit.
bootstrap.releaseExternalResources();
}
private static ChannelFuture writeSecureSampleData(Channel channel, ChannelBuffer buffer) throws Exception {
long gameId = 1234;
ChannelBuffer buf = ChannelBuffers.buffer(256);
buf.writeLong(gameId);
writeParamsForLogin(buf);
byte [] data = buf.array();
byte [] encryptedData = EncryptionManager.getInstance().encrypt(data, EncryptionManager.getInstance().getPublicKey());
int size = encryptedData.length;
buffer.writeInt(size);
buffer.writeBytes(encryptedData);
ChannelFuture writeFuture = channel.write(buffer);
return writeFuture;
}
private static void writeParamsForLogin(ChannelBuffer buf) {
int eventHash = getEventHash("Login");
buf.writeInt(eventHash);
int paramCount = 3 ; // in case of PlayerToken 2 parameters to be send : player id + player token
buf.writeInt(paramCount);
// version , E , N
String version = "1.0";
buf.writeInt(dataType_String);
buf.writeInt(version.length());
buf.writeBytes(version.getBytes());
String E = "61";
buf.writeInt(dataType_ByteArray);
buf.writeInt(E.length());
buf.writeBytes(E.getBytes());
String N = "53";
buf.writeInt(dataType_ByteArray);
buf.writeInt(N.length());
buf.writeBytes(N.getBytes());
}
}
and the client handler class :
public class ClientHandler extends SimpleChannelUpstreamHandler {
#Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) {
System.err.println(e.getMessage());
System.out.println("Message Recieved");
ChannelBuffer buf = (ChannelBuffer)e.getMessage();
while (buf.readable()) {
System.out.println((char) buf.readByte());
System.out.flush();
}
}
#Override
public void exceptionCaught(
ChannelHandlerContext ctx, ExceptionEvent e) {
logger.log(
Level.WARNING,
"Unexpected exception from downstream.",
e.getCause());
e.getCause().printStackTrace();
e.getChannel().close();
}
}
On receiving the request data on the server, I am processing it and writing back the response using the following method :
private static boolean sendMessage(NetworkClient targetObject, String eventName, Object[] params) {
logger.debug("Sending message for the event : " + eventName);
if(targetObject == null) {
sendMessageToAll(eventName, params);
} else {
if (targetObject.getClientChannel() == null) {
logger.error("Target not defined.");
return false;
}
Channel clientChannel = targetObject.getClientChannel();
ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
long hash = getHashString(eventName);
buf.writeInt(512);
buf.writeLong(hash);
if(params != null) {
buf.writeInt(params.length);
for(Object param : params) {
int type = getTypeOfObject(param);
buf.writeInt(type);
writeParamToBuffer(buf, type, param);
}
}
ChannelFuture cf = null;
try {
cf = clientChannel.write(buf);
if(cf.isSuccess()){
System.out.println("Written to client successfully");
}
} catch (Exception e) {
logger.error("Error in broadcasting for event : " + eventName, e);
} finally {
}
}
return false;
}
This code is still a work in progress, so you might find lot of "not required" stuff in there. I just wanted to show the logic that I am trying to use and want to know why it is not working.
I have taken help from examples at http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/ for writing this.
Thanks in advance for any help.
Very complete code.
An easy way to write text to a websockets channel is to do this:
ChannelFuture writeFuture = channel.write(new TextWebSocketFrame("My Text here!");
You don't need to deal with the ChannelBuffers directly. That may resolve your problem.
Due to a variety of reasons it became necessary to create our own proxy. Everything is working as it should through HTTP. As soon as we receive a CONNECT to tunnel through SSL is when everything goes wrong. What we do logically is take the CONNECT parse out the host and port so we know where we are sending future ssl requests and create a request to send back to the browser stating we have successfully made the ssl handshake like so:
HTTP/1.0 200 Connection established\r\nProxy-agent: test\r\n\r\n
What we expect to happen is that the browser once receiving this successful message will send us the next https request. However, instead we get sent another CONNECT request over and over.It is clear that is does not like the response we send back. The problem is that i'm not exactly sure why? Does the response back need to be sent back via an https socket? I just don't understand this process enough to move forward.
Here is my server class:
public class HttpServer extends Observable implements IWebServer, Runnable
{
int Port = -1;
int State = HttpState.IDLE;
ArrayList<WebTransactionEvent> History = new ArrayList<WebTransactionEvent>();
ArrayList<HttpService> myServices = new ArrayList<HttpService>();
SocketChannel myChannel = null;
boolean needResponse = false;
boolean shouldStop;
Logger logger = OpsToolsLogger.getLogger(HttpServer.class.getName());
Selector selector ;
static Hashtable<String, HttpServer> myInstances = new Hashtable<String, HttpServer>();
Hashtable<HttpTransaction, HttpService> myTaskTable = new Hashtable<HttpTransaction, HttpService>();
Vector<HttpTransaction> transactionQueue = new Vector<HttpTransaction>();
private HttpServer(){}
private HttpServer(int Port)
{
logger.log(Level.WARNING, "HttpServer: startup - listening to port: " + Port);
this.Port = Port;
shouldStop = false;
// Create the selector
try {
selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(Port));
this.registerSocket(serverChannel);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(this).start();
}
public static HttpServer getInstance(String port)
{
if( !myInstances.containsKey( port ) )
{
myInstances.put( port, new HttpServer(Integer.parseInt(port)));
}
return myInstances.get(port);
}
public int getState()
{
return State;
}
public void stop()
{
shouldStop = true;
}
public boolean needResponse()
{
return needResponse;
}
public HttpTransaction getNextTransaction()
{
if(transactionQueue.isEmpty())
{
return null;
}
//System.out.println("grabbing next trans");
HttpTransaction temp = transactionQueue.firstElement();
transactionQueue.remove(0);//pop trans from queue
return temp;
}
public void dropTransaction()
{
myTaskTable.clear();
needResponse = false;
}
public synchronized boolean respond(HttpTransaction transaction, IHttpResponse editedResponse, boolean closeConnection)
{
logger.log(Level.FINE, "HttpServer: responding ");
needResponse = false;
if(myTaskTable.isEmpty())
{
return false;
}
//see if there isn't a service object registered with that transaction
if(!myTaskTable.containsKey(transaction))
{
return false;
}
State = HttpState.SENDING_RESPONSE;
ManipulatedHttpTransaction myTrans = (ManipulatedHttpTransaction) transaction;
HttpResponse response = (HttpResponse) editedResponse;
myTrans.setManipulatedResponse( response );
HttpService serv = myTaskTable.get(transaction);
if(!serv.respond(myTrans.getManipulatedResponse(), closeConnection))
{
History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_ERROR ) );
return false;
}
myTaskTable.remove(transaction);
History.add( new WebTransactionEvent( myTrans, WebTransactionEvent.TRANSACTION_COMPLETED ) );
needResponse = !myTaskTable.isEmpty();
return true;
}
public void registerSocket(ServerSocketChannel theSocket)
{
try {
theSocket.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try {
while (!shouldStop ) {
// Wait for an event
selector.select();
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator();
// Process each key
while (it.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)it.next();
// Remove it from the list to indicate that it is being processed
it.remove();
// Check if it's a connection request
if (selKey.isAcceptable()) {
// Get channel with connection request
ServerSocketChannel ssChannel = (ServerSocketChannel)selKey.channel();
SocketChannel theChannel = ssChannel.accept();
if(theChannel != null)
{
logger.log(Level.FINEST, "HttpServer: Connection established");
try
{
theChannel.configureBlocking(false);
}
catch(Exception e)
{
logger.log(Level.WARNING, "myChannel = null ( configureBlocking() )");
//bytesRead = -1;
}
myServices.add( new HttpService(this, theChannel ) );
needResponse = true;
}
//needResponse = !myTaskTable.isEmpty();
//System.out.println("need response: "+ needResponse);
}
}
}
} catch (IOException e) {
}
//shutdown
logger.log(Level.WARNING, "Server stopping - " + Port);
}
public ArrayList<WebTransactionEvent> getHistory()
{
return new ArrayList<WebTransactionEvent>(History);
}
public boolean switchServerToSSL()
{
//HttpService tempService = myTaskTable.get(PendingTransaction);
//tempService.useSSL = true;
return true;
}
/**
* Adds the transaction from browser to the transaction queue and also ties it to a service by adding it to myTasks map
* #param myTrans
* #param httpService
*/
public void addTransaction(ManipulatedHttpTransaction myTrans,
HttpService httpService) {
// TODO Auto-generated method stub
//ensure vector has room to add another transaction
if(transactionQueue.capacity() <= transactionQueue.size())
transactionQueue.ensureCapacity(transactionQueue.size() * 2);
transactionQueue.add(myTrans);//add transaction to queue
myTaskTable.put(myTrans, httpService);//tie the transaction toits service
// System.out.println("server notifying proxy: " + myTrans.getFullURL());
this.setChanged();
this.notifyObservers(myTrans);
}
}
Here is portion in proxy that handles a CONNECT:
if(tempTransaction.getOriginatingRequest().getMethod().contentEquals("CONNECT"))
{
/*tell the browser that the connection exists
*
* Each time you connect to an SSL-protected website, Burp generates a server certificate for that host, signed by the CA certificate
*
* The server certificates presented to the client (i.e. a web browser) are dynamically generated/signed by the proxy and contain most of the same fields as the original webserver certificate. The subject DN, serial number, validity dates, and extensions are preserved. However, the issuer DN is now set to the name of the proxy's self-signed
* certificate and the public/private keys of the proxy are used in creating the forged certificate. These forged certificates are cached (in memory) by the proxy, for better performance
*/
HttpResponse tunnelResponse = new HttpResponse("HTTP/1.0 200 Connection established\r\nProxy-agent: Ops Assistant\r\n\r\n");
tempTransaction.setResponse(tunnelResponse);
if(!finishResponse2(tempTransaction,tempTransaction.getResponse(), false));
{
//close the connection
}
myServer.switchServerToSSL();
}
Here is section sends request back to browser:
public boolean respond(IHttpResponse response, boolean closeConnection)
{
isCloseConnectionRequested = closeConnection;
try
{
if(useSSL)
{
ByteBuffer tmpBuffer = response.getData();
tmpBuffer.position(0);
myConnection.SecureWrite( tmpBuffer );
}
else
{
ByteBuffer tmpBuffer = response.getData();
tmpBuffer.position(0);
myConnection.Write(tmpBuffer);
}
if(closeConnection)
{
myChannel.close();
myChannel = null;
}
}
catch (Exception e)
{
isResponded = true;
return false;
}
isResponded = true;
return true;
}
Probably most important the socket class:
public class SocketConnection implements IConnection
{
public SocketChannel theSocketChannel;
public InetSocketAddress theRemoteAddress;
public int TimeoutThreshold;
private int TimeOutThreshold = 30;
private SSLEngine theSSLEngine;
private SSLContext theSSLContext;
private ByteBuffer inNetworkDataBuffer;
private ByteBuffer inAppDataBuffer;
private ByteBuffer outNetworkDataBuffer;
private ByteBuffer outAppDataBuffer;
//create outbound connection to host/port
public SocketConnection(String Host, int Port ) throws IOException
{
theRemoteAddress = new InetSocketAddress( Host, Port);
theSocketChannel = SocketChannel.open();
theSocketChannel.configureBlocking(false);
theSocketChannel.connect( theRemoteAddress );
theSocketChannel.finishConnect();
}
//use existing socket connection
public SocketConnection(SocketChannel existingChannel) throws IOException
{
theSocketChannel = existingChannel;
theSocketChannel.configureBlocking(false);
theRemoteAddress = new InetSocketAddress( existingChannel.socket().getInetAddress(), existingChannel.socket().getPort() );
}
public boolean setTimeOut(int newTimeOutThreshold)
{
TimeOutThreshold = newTimeOutThreshold;
return true;
}
public void waitForSocketToConnect() throws Exception
{
int i = 0;
while( !this.isConnected() )
{
this.finishConnect();
if(i>=3000)
{
throw new Exception();
}
i++;
try{Thread.sleep(10);}catch(Exception e){}
}
}
public boolean Write( ByteBuffer DataToSend )
{
try
{
//DataToSend.flip();
int numBytesWritten = theSocketChannel.write(DataToSend);
try
{
DataToSend.compact();
}
catch (ReadOnlyBufferException e)
{
DataToSend.rewind();
}
}
catch (IOException e)
{
// Connection may have been closed
}
return true;
}
public ByteBuffer Read()
{
ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);
try
{
ByteBuffer netBuffer = ByteBuffer.wrap(new byte[10000]);
// Clear the buffer and read bytes from socket
netBuffer.clear();
int numBytesRead = theSocketChannel.read(netBuffer);
if(numBytesRead == -1)
return null; //-1 means we done return null as the flag
netBuffer.flip();
ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + netBuffer.limit() ]);
ResponseBytes.position(0);
netBuffer.position(0);
tempBuffer.put(ResponseBytes);
tempBuffer.put(netBuffer);
netBuffer.flip();
ResponseBytes = tempBuffer;
}
catch (IOException e)
{
// Connection may have been closed
e = e;
return ByteBuffer.wrap( e.getMessage().getBytes() );
}
return (ByteBuffer) ResponseBytes.flip();
}
public boolean SecureWrite( ByteBuffer DataToSend )
{
boolean writeSuccess = true;
try
{
//if we don't have a SSLEngine make one
if(theSSLEngine==null)
{
setupSSL();
}
//Convert Data
outAppDataBuffer.clear();
outAppDataBuffer.put(DataToSend);
outAppDataBuffer.flip();
SSLEngineResult sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
outAppDataBuffer.compact();
//outNetworkDataBuffer.flip();
//int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
if(sslResult.getStatus() == SSLEngineResult.Status.OK)
{
if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
{
// Write bytes
outNetworkDataBuffer.flip();
int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
outNetworkDataBuffer.compact();
if(finishHandshake(sslResult))
{
DataToSend.rewind();
return SecureWrite(DataToSend);
}
else
{
return false;
}
}
else
{
// Write bytes
outNetworkDataBuffer.rewind();
Write(outNetworkDataBuffer);
}
}
else
{
}
}
catch(Exception e)
{
writeSuccess = false;
}
return writeSuccess;
}
public ByteBuffer SecureRead() throws ReadTimedOutException
{
int timeElapsed = 0;
ByteBuffer ResponseBytes = ByteBuffer.allocateDirect(0);
try
{
//if we don't have a SSLEngine make one
if(theSSLEngine==null)
{
setupSSL();
}
int consumedCount = 0;
SSLEngineResult sslResult;
do
{
//inNetworkDataBuffer.clear();
inNetworkDataBuffer.put( Read() );
inNetworkDataBuffer.flip();
sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
consumedCount += sslResult.bytesConsumed();
inNetworkDataBuffer.compact();
if( sslResult.getStatus() == SSLEngineResult.Status.OK )
{
if(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
{
if(finishHandshake(sslResult))
{
return SecureRead();
}
else
{
return ByteBuffer.allocateDirect(0);
}
}
else
{
timeElapsed = 0;
inAppDataBuffer.flip();
ByteBuffer tempBuffer = ByteBuffer.wrap(new byte[ ResponseBytes.limit() + inAppDataBuffer.limit() ]);
ResponseBytes.position(0);
inAppDataBuffer.position(0);
tempBuffer.put(ResponseBytes);
tempBuffer.put(inAppDataBuffer);
inAppDataBuffer.flip();
ResponseBytes = tempBuffer;
ResponseBytes.flip();
}
}
else
{
//the status wasn't ok
timeElapsed++;
}
}while(consumedCount < inNetworkDataBuffer.limit() && sslResult.getStatus() != SSLEngineResult.Status.OK);
}
catch (Exception e)
{
System.out.println(e.toString());
}
if(timeElapsed>=TimeOutThreshold)
{
throw new ReadTimedOutException();
}
return ResponseBytes;
}
public boolean Disconnect()
{
try
{
theSocketChannel.close();
}
catch(Exception e)
{
return false;
}
return true;
}
public boolean isClosed()
{
return !theSocketChannel.isOpen();
}
#Override
public String getHost()
{
return theRemoteAddress.getHostName();
}
#Override
public int getPort()
{
return theRemoteAddress.getPort();
}
public boolean isConnected()
{
return theSocketChannel.isConnected();
}
#Override
public boolean hasSecure()
{
return true;
}
public boolean finishConnect() throws Exception
{
return theSocketChannel.finishConnect();
}
private void setupSSL() throws NoSuchAlgorithmException, KeyManagementException
{
//create a new SSLEngine instance
System.setProperty( "javax.net.debug", "ssl");
TrustManager[] tm = new TrustManager[] { new NaiveTrustManager() };
SSLContext theSSLContext = SSLContext.getInstance ("TLS");
theSSLContext.init( new KeyManager[0], tm, new SecureRandom( ) );
theSSLEngine = theSSLContext.createSSLEngine( theRemoteAddress.getHostName(), theRemoteAddress.getPort());
theSSLEngine.setUseClientMode(true);
inNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
inAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);
outNetworkDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getPacketBufferSize()]);
outAppDataBuffer = ByteBuffer.wrap(new byte[theSSLEngine.getSession().getApplicationBufferSize()]);
}
private boolean finishHandshake(SSLEngineResult sslResult)
{
boolean bFinished = false;
while(sslResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED)
{
if( sslResult.getStatus() == SSLEngineResult.Status.CLOSED )
{
bFinished = false;
//break;
}
if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK)
{
Runnable task;
while ((task=theSSLEngine.getDelegatedTask()) != null)
{
task.run();
}
try
{
//outNetworkDataBuffer.flip();
sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
//outNetworkDataBuffer.compact();
}
catch (SSLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
{
try
{
outAppDataBuffer.flip();
sslResult = theSSLEngine.wrap(outAppDataBuffer, outNetworkDataBuffer);
outAppDataBuffer.compact();
} catch (SSLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if((sslResult.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) || (outNetworkDataBuffer.position() > 0))
{
try
{
outNetworkDataBuffer.flip();
int numBytesWritten = theSocketChannel.write(outNetworkDataBuffer);
outNetworkDataBuffer.compact();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
else if(sslResult.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)
{
try
{
int numBytes;
//read data from the socket into inNetworkBuffer
inNetworkDataBuffer.flip();
sslResult = theSSLEngine.unwrap( inNetworkDataBuffer, inAppDataBuffer );
inNetworkDataBuffer.compact();
if(theSSLEngine.isInboundDone())
{
}
else
{
numBytes = theSocketChannel.read(inNetworkDataBuffer);
numBytes = numBytes;
}
}
catch (Exception e)
{
e.printStackTrace();
return false;
}
}
}
return true;
}
}
Anyone have any tips on how to best establish this handshake with the browser?
Have you read the Internet draft? The CONNECT is received in plaintext. You form the upstream connection and return the 'HTTP/1.0 200 Connection established' response. After that the proxy isn't processing requests and responses, it is just copying bytes in both directions, whatever they may happen to be. Specifically, the proxy isn't concerned with SSL in any way shape or form.