/*
* Copyright (c) 2012 Alexander Diener,
*
* 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 de.fhkn.in.uce.relaying.server;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.fhkn.in.uce.relaying.message.RelayingAttributeTypeDecoder;
import de.fhkn.in.uce.relaying.message.RelayingMethod;
import de.fhkn.in.uce.relaying.message.RelayingMethodDecoder;
import de.fhkn.in.uce.stun.attribute.AttributeTypeDecoder;
import de.fhkn.in.uce.stun.attribute.ErrorCode.STUNErrorCode;
import de.fhkn.in.uce.stun.header.MessageMethodDecoder;
import de.fhkn.in.uce.stun.message.Message;
import de.fhkn.in.uce.stun.message.MessageReader;
import de.fhkn.in.uce.stun.message.MessageWriter;
/**
* Task that reads messages from the clients socket and handles them. Can handle
* allocation requests and and connection bind requests.
*
* @author thomas zink, daniel maier, Alexander Diener
* (aldiener@htwg-konstanz.de)
*
*/
public class MessageDispatcherTask implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(MessageDispatcherTask.class);
private final Socket s;
private final MessageWriter controlConnectionWriter;
private final Map<UUID, BlockingQueue<Socket>> connIDToQueue;
// has to be unbounded
private final Executor controlConnectionHandlerExecutor;
// has to be unbounded
private final Executor relayExecutor;
/**
* Creates a new {@link MessageDispatcherTask}.
*
* @param s
* the socket to the client
* @param connIDToQueue
* map to match relay connection between client and peers
* @param controlConnectionHandlerExecutor
* the executor that gets used to execute the
* {@link RefreshMessageHandlerTask} for the given control
* connection
* @param relayExecutor
* the executor that gets used to execute task for the real relay
* stuff
* @throws IOException
* if an I/O error occurs while getting the output stream of the
* socket to the client
*/
public MessageDispatcherTask(Socket s, Map<UUID, BlockingQueue<Socket>> connIDToQueue,
Executor controlConnectionHandlerExecutor, Executor relayExecutor) throws IOException {
this.s = s;
this.controlConnectionWriter = new MessageWriter(s.getOutputStream());
this.connIDToQueue = connIDToQueue;
this.controlConnectionHandlerExecutor = controlConnectionHandlerExecutor;
this.relayExecutor = relayExecutor;
}
/**
* Reads the message from the input stream of the socket to the client. Then
* distinguishes two messages: allocation request and connection bind
* request. If the message is an allocation request an
* {@link RelayAllocationHandler} gets used to handle the message. Else if
* the message is an connection bind request a {@link ConnectionBindHandler}
* gets used to handle the message. If the message was of unknown type a bad
* request error is returned to the client.
*/
public void run() {
Message message;
try {
final MessageReader messageReader = this.createCustomRelayingMessageReader();
logger.debug("Reading incoming message from {}", s.toString()); //$NON-NLS-1$
message = messageReader.readSTUNMessage(s.getInputStream());
} catch (IOException e) {
logger.error("IOEXception while receiving message: {}", e.getMessage());
return;
}
if (message.isMethod(RelayingMethod.ALLOCATION) && message.isRequest()) {
logger.info("Received allocation request");
new RelayAllocationHandler(s, controlConnectionWriter, connIDToQueue, message,
controlConnectionHandlerExecutor, relayExecutor).handle();
} else if (message.isMethod(RelayingMethod.CONNECTION_BIND) && message.isRequest()) {
logger.info("Received connection bind");
new ConnectionBindHandler(s, message, connIDToQueue).handle();
} else {
// unknown message
logger.error("Received wrong message tye {}", message.getMessageMethod());
try {
Message errorResponse = message.buildFailureResponse(STUNErrorCode.BAD_REQUEST,
"Did not expect message " + message.getMessageMethod());
controlConnectionWriter.writeMessage(errorResponse);
} catch (IOException e) {
logger.error("IOException while sending error response");
}
}
}
private MessageReader createCustomRelayingMessageReader() {
logger.debug("Creating custom relaying message reader"); //$NON-NLS-1$
final List<MessageMethodDecoder> customMethodDecoders = new ArrayList<MessageMethodDecoder>();
customMethodDecoders.add(new RelayingMethodDecoder());
final List<AttributeTypeDecoder> customAttributeTypeDecoders = new ArrayList<AttributeTypeDecoder>();
customAttributeTypeDecoders.add(new RelayingAttributeTypeDecoder());
return MessageReader.createMessageReaderWithCustomDecoderLists(customMethodDecoders,
customAttributeTypeDecoders);
}
}