package voldemort.server.socket; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Map; import org.apache.log4j.Logger; import voldemort.VoldemortException; import voldemort.client.protocol.RequestFormatType; import voldemort.server.protocol.RequestHandler; import voldemort.server.protocol.RequestHandlerFactory; import voldemort.server.protocol.StreamRequestHandler; import voldemort.server.protocol.StreamRequestHandler.StreamRequestHandlerState; import voldemort.utils.ByteUtils; /** * Represents a session of interaction between the server and the client. This * begins with protocol negotiation and then a series of client requests * followed by server responses. The negotiation is handled by the session * object, which will choose an appropriate request handler to handle the actual * request/response. * * */ public class SocketServerSession implements Runnable { private final Logger logger = Logger.getLogger(SocketServerSession.class); private final Map<Long, SocketServerSession> activeSessions; private final long sessionId; private final Socket socket; private final RequestHandlerFactory handlerFactory; private volatile boolean isClosed = false; public SocketServerSession(Map<Long, SocketServerSession> activeSessions, Socket socket, RequestHandlerFactory handlerFactory, long id) { this.activeSessions = activeSessions; this.socket = socket; this.handlerFactory = handlerFactory; this.sessionId = id; } public Socket getSocket() { return socket; } private boolean isInterrupted() { return Thread.currentThread().isInterrupted(); } public void run() { try { activeSessions.put(sessionId, this); DataInputStream inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream(), 64000)); DataOutputStream outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream(), 64000)); RequestFormatType protocol = negotiateProtocol(inputStream, outputStream); RequestHandler handler = handlerFactory.getRequestHandler(protocol); logger.info("Client " + socket.getRemoteSocketAddress() + " connected successfully with protocol " + protocol.getCode()); while(!isInterrupted() && !socket.isClosed() && !isClosed) { StreamRequestHandler srh = handler.handleRequest(inputStream, outputStream); if(srh != null) { if(logger.isTraceEnabled()) logger.trace("Request is streaming"); StreamRequestHandlerState srhs = null; try { do { if(logger.isTraceEnabled()) logger.trace("About to enter streaming request handler"); srhs = srh.handleRequest(inputStream, outputStream); if(logger.isTraceEnabled()) logger.trace("Finished invocation of streaming request handler, result is " + srhs); } while(srhs != StreamRequestHandlerState.COMPLETE); } catch(VoldemortException e) { srh.handleError(outputStream, e); outputStream.flush(); break; } finally { srh.close(outputStream); } } outputStream.flush(); } if(isInterrupted()) logger.info(Thread.currentThread().getName() + " has been interrupted, closing session."); } catch(EOFException e) { logger.info("Client " + socket.getRemoteSocketAddress() + " disconnected."); } catch(IOException e) { // if this is an unexpected if(!isClosed) logger.error(e); } finally { try { if(!socket.isClosed()) socket.close(); } catch(Exception e) { logger.error("Error while closing socket", e); } // now remove ourselves from the set of active sessions this.activeSessions.remove(sessionId); } } private RequestFormatType negotiateProtocol(InputStream input, OutputStream output) throws IOException { input.mark(3); byte[] protoBytes = new byte[3]; ByteUtils.read(input, protoBytes); RequestFormatType requestFormat; try { String proto = ByteUtils.getString(protoBytes, "UTF-8"); requestFormat = RequestFormatType.fromCode(proto); output.write(ByteUtils.getBytes("ok", "UTF-8")); output.flush(); } catch(IllegalArgumentException e) { // okay we got some nonsense. For backwards compatibility, // assume this is an old client who does not know how to negotiate requestFormat = RequestFormatType.VOLDEMORT_V0; // reset input stream so we don't interfere with request format input.reset(); logger.info("No protocol proposal given, assuming " + RequestFormatType.VOLDEMORT_V0.getDisplayName()); } return requestFormat; } public void close() throws IOException { this.isClosed = true; this.socket.close(); } }