/* * Copyright (C) 2011 Roderick Baier * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.roderick.weberknecht; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.Socket; import java.net.URI; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; public class WebSocketConnection implements WebSocket { private URI url = null; private WebSocketEventHandler eventHandler = null; private volatile boolean connected = false; private Socket socket = null; private InputStream input = null; private BufferedOutputStream output = null; private WebSocketReceiver receiver = null; private WebSocketHandshake handshake = null; public WebSocketConnection(URI url) throws WebSocketException { this(url, null); } public WebSocketConnection(URI url, String protocol) throws WebSocketException { this.url = url; handshake = new WebSocketHandshake(url, protocol); } public void setEventHandler(WebSocketEventHandler eventHandler) { this.eventHandler = eventHandler; } public WebSocketEventHandler getEventHandler() { return this.eventHandler; } public void connect() throws WebSocketException { try { if (connected) { throw new WebSocketException("already connected"); } socket = createSocket(); input = socket.getInputStream(); output = new BufferedOutputStream(socket.getOutputStream()); output.write(handshake.getHandshake()); output.flush(); boolean handshakeComplete = false; boolean header = true; int len = 2000; byte[] buffer = new byte[len]; int pos = 0; ArrayList<String> handshakeLines = new ArrayList<String>(); byte[] serverResponse = new byte[16]; while (!handshakeComplete) { int b = input.read(); buffer[pos] = (byte) b; pos += 1; if (!header) { serverResponse[pos-1] = (byte)b; if (pos == 16) { handshakeComplete = true; } } else if (buffer[pos-1] == 0x0A && buffer[pos-2] == 0x0D) { String line = new String(buffer, "UTF-8"); if (line.trim().equals("")) { header = false; } else { handshakeLines.add(line.trim()); } buffer = new byte[len]; pos = 0; } } handshake.verifyServerStatusLine(handshakeLines.get(0)); handshake.verifyServerResponse(serverResponse); handshakeLines.remove(0); HashMap<String, String> headers = new HashMap<String, String>(); for (String line : handshakeLines) { String[] keyValue = line.split(": ", 2); headers.put(keyValue[0], keyValue[1]); } handshake.verifyServerHandshakeHeaders(headers); receiver = new WebSocketReceiver(input, this); receiver.start(); connected = true; eventHandler.onOpen(); } catch (WebSocketException wse) { throw wse; } catch (IOException ioe) { throw new WebSocketException("error while connecting: " + ioe.getMessage(), ioe); } } public synchronized void send(String data) throws WebSocketException { if (!connected) { throw new WebSocketException("error while sending text data: not connected"); } try { output.write(0x00); output.write(data.getBytes(("UTF-8"))); output.write(0xff); output.flush(); } catch (UnsupportedEncodingException uee) { throw new WebSocketException("error while sending text data: unsupported encoding", uee); } catch (IOException ioe) { throw new WebSocketException("error while sending text data", ioe); } } public void handleReceiverError() { try { if (connected) { close(); } } catch (WebSocketException wse) { wse.printStackTrace(); } } public synchronized void close() throws WebSocketException { if (!connected) { return; } sendCloseHandshake(); if (receiver.isRunning()) { receiver.stopit(); } closeStreams(); eventHandler.onClose(); } private synchronized void sendCloseHandshake() throws WebSocketException { if (!connected) { throw new WebSocketException("error while sending close handshake: not connected"); } try { output.write(0xff); output.write(0x00); output.flush(); } catch (IOException ioe) { throw new WebSocketException("error while sending close handshake", ioe); } connected = false; } private Socket createSocket() throws WebSocketException { String scheme = url.getScheme(); String host = url.getHost(); int port = url.getPort(); Socket socket = null; if (scheme != null && scheme.equals("ws")) { if (port == -1) { port = 80; } try { socket = new Socket(host, port); } catch (UnknownHostException uhe) { throw new WebSocketException("unknown host: " + host, uhe); } catch (IOException ioe) { throw new WebSocketException("error while creating socket to " + url, ioe); } } else if (scheme != null && scheme.equals("wss")) { if (port == -1) { port = 443; } try { SocketFactory factory = SSLSocketFactory.getDefault(); socket = factory.createSocket(host, port); } catch (UnknownHostException uhe) { throw new WebSocketException("unknown host: " + host, uhe); } catch (IOException ioe) { throw new WebSocketException("error while creating secure socket to " + url, ioe); } } else { throw new WebSocketException("unsupported protocol: " + scheme); } return socket; } private void closeStreams() throws WebSocketException { try { input.close(); output.close(); socket.close(); } catch (IOException ioe) { throw new WebSocketException("error while closing websocket connection: ", ioe); } } }