// --------------------------------------------------------------------------- // jWebSocket - Jetty Connector // Copyright (c) 2010 Alexander Schulze, Innotrade GmbH // --------------------------------------------------------------------------- // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser 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 Lesser General Public License for // more details. // You should have received a copy of the GNU Lesser General Public License along // with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>. // --------------------------------------------------------------------------- package org.jwebsocket.jetty; import java.net.InetAddress; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javolution.util.FastMap; import org.apache.log4j.Logger; import org.eclipse.jetty.websocket.WebSocket.Outbound; import org.jwebsocket.api.WebSocketEngine; import org.jwebsocket.api.WebSocketPacket; import org.jwebsocket.async.IOFuture; import org.jwebsocket.config.JWebSocketCommonConstants; import org.jwebsocket.connectors.BaseConnector; import org.jwebsocket.kit.CloseReason; import org.jwebsocket.kit.RawPacket; import org.jwebsocket.kit.RequestHeader; import org.jwebsocket.kit.RequestHeader; import org.jwebsocket.logging.Logging; /** * * @author aschulze */ public class JettyConnector extends BaseConnector { private static Logger mLog = Logging.getLogger(JettyConnector.class); private boolean mIsRunning = false; private CloseReason mCloseReason = CloseReason.TIMEOUT; private Outbound mOutbound; private HttpServletRequest mRequest = null; private String mProtocol = null; /** * creates a new TCP connector for the passed engine using the passed client * socket. Usually connectors are instantiated by their engine only, not by * the application. * * @param aEngine * @param aClientSocket */ public JettyConnector(WebSocketEngine aEngine, HttpServletRequest aRequest, String aProtocol, Outbound aOutbound) { super(aEngine); mOutbound = aOutbound; mRequest = aRequest; mProtocol = aProtocol; Map<String, String> lArgs = new FastMap<String, String>(); String lPath = (String) aRequest.getRequestURL().toString(); if (mLog.isDebugEnabled()) { mLog.debug("Requesting '" + lPath + "', " + (aProtocol != null ? "'" + aProtocol + "'" : "[w/o protocol]") + "..."); } // isolate search string String lSearchString = ""; if (lPath != null) { int lPos = lPath.indexOf(JWebSocketCommonConstants.PATHARG_SEPARATOR); if (lPos >= 0) { lSearchString = lPath.substring(lPos + 1); if (lSearchString.length() > 0) { String[] lArgsArray = lSearchString.split(JWebSocketCommonConstants.ARGARG_SEPARATOR); for (int lIdx = 0; lIdx < lArgsArray.length; lIdx++) { String[] lKeyValuePair = lArgsArray[lIdx].split(JWebSocketCommonConstants.KEYVAL_SEPARATOR, 2); if (lKeyValuePair.length == 2) { lArgs.put(lKeyValuePair[0], lKeyValuePair[1]); if (mLog.isDebugEnabled()) { mLog.debug("arg" + lIdx + ": " + lKeyValuePair[0] + "=" + lKeyValuePair[1]); } } } } } } RequestHeader lHeader = new RequestHeader(); // set default sub protocol if none passed if (aProtocol == null) { aProtocol = JWebSocketCommonConstants.WS_SUBPROT_DEFAULT; } lHeader.put(RequestHeader.WS_PROTOCOL, aProtocol); // lHeader.put(RequestHeader.WS_HOST, lRespMap.get(RequestHeader.WS_HOST)); // lHeader.put(RequestHeader.WS_ORIGIN, lRespMap.get(RequestHeader.WS_ORIGIN)); // lHeader.put(RequestHeader.WS_LOCATION, lRespMap.get(RequestHeader.WS_LOCATION)); // lHeader.put(RequestHeader.WS_PATH, lRespMap.get("path")); lHeader.put(RequestHeader.WS_SEARCHSTRING, lSearchString); lHeader.put(RequestHeader.URL_ARGS, lArgs); setHeader(lHeader); } @Override public void startConnector() { if (mLog.isDebugEnabled()) { mLog.debug("Starting Jetty connector..."); } mIsRunning = true; if (mLog.isInfoEnabled()) { mLog.info("Started Jetty connector."); } // call connectorStarted method of engine getEngine().connectorStarted(this); } @Override public void stopConnector(CloseReason aCloseReason) { if (mLog.isDebugEnabled()) { mLog.debug("Stopping Jetty connector (" + aCloseReason.name() + ")..."); } // call client stopped method of engine // (e.g. to release client from streams) // getEngine().connectorStopped(this, aCloseReason); mOutbound.disconnect(); mCloseReason = aCloseReason; mIsRunning = false; if (mLog.isInfoEnabled()) { mLog.info("Stopped Jetty connector."); } } @Override public void processPacket(WebSocketPacket aDataPacket) { // forward the data packet to the engine // the engine forwards the packet to all connected servers getEngine().processPacket(this, aDataPacket); } @Override public synchronized void sendPacket(WebSocketPacket aDataPacket) { if (mLog.isDebugEnabled()) { mLog.debug("Sending packet '" + aDataPacket.getUTF8() + "'..."); } try { if (aDataPacket.getFrameType() == RawPacket.FRAMETYPE_BINARY) { mOutbound.sendMessage((byte) 0, aDataPacket.getByteArray()); } else { mOutbound.sendMessage(aDataPacket.getUTF8()); } } catch (Exception lEx) { mLog.error(lEx.getClass().getSimpleName() + " sending data packet: " + lEx.getMessage()); } if (mLog.isDebugEnabled()) { mLog.debug("Packet '" + aDataPacket.getUTF8() + "' sent."); } } @Override public IOFuture sendPacketAsync(WebSocketPacket aDataPacket) { throw new UnsupportedOperationException("Underlying connector:" + getClass().getName() + " doesn't support asynchronous send operation"); } @Override public String generateUID() { String lUID = getRemoteHost() + "@" + getRemotePort(); return lUID; } @Override public int getRemotePort() { return mRequest.getRemotePort(); } @Override public InetAddress getRemoteHost() { InetAddress lAddr; try { lAddr = InetAddress.getByName(mRequest.getRemoteAddr()); } catch (Exception lEx) { lAddr = null; } return lAddr; } @Override public String toString() { // TODO: weird results like... '0:0:0:0:0:0:0:1:61130'... on JDK 1.6u19 // Windows 7 64bit String lRes = getRemoteHost().getHostAddress() + ":" + getRemotePort(); // TODO: don't hard code. At least use JWebSocketConstants field here. String lUsername = getString("org.jwebsocket.plugins.system.username"); if (lUsername != null) { lRes += " (" + lUsername + ")"; } return lRes; } }