Android Open Source - ubihelper Client Websocket






From Project

Back to project page ubihelper.

License

The source code is released under:

GNU General Public License

If you think the Android project ubihelper listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * Copyright (c) 2012 The University of Nottingham
 * //from  ww  w  .  j av a 2 s  .c  o m
 * This file is part of ubihelper
 *
 *  ubihelper is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  ubihelper 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 Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with ubihelper. If not, see <http://www.gnu.org/licenses/>.
 *  
 *  @author Chris Greenhalgh (cmg@cs.nott.ac.uk), The University of Nottingham
 */
package uk.ac.horizon.ubihelper.websocket;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.security.SecureRandom;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author cmg
 *
 */
public class ClientWebsocket extends SocketChannelWebsocket {
  static Logger logger = Logger.getLogger(ClientWebsocket.class.getName());
  /** non-secure / http */
  static final int DEFAULT_PORT = 80; 
  private boolean headerSent = false;
  private boolean responseReceived = false;
  private URI url;
  private byte [] nonce;
  private static SecureRandom srandom;
  private static Random random;
  static {
    random = new Random(System.currentTimeMillis());
    try {
      srandom = SecureRandom.getInstance("SHA1PRNG");
    }
    catch (Exception e) {
      logger.warning("Could not get SecureRandom: "+e);
    }
  }
  public ClientWebsocket(URI url, Selector selector, ReentrantLock selectorLock, WebsocketListener listener) throws IOException {
    super(ReadyState.CONNECTING, selector, selectorLock, listener);
    this.url = url;
    // TODO initialise TCP connection using SocketChannel to server
    int port = url.getPort();
    if (port<0)
      port = DEFAULT_PORT;
    connect(url.getHost(), port);
    // TODO create HTTP request header and schedule for sending
    // TODO configure suitable hooks to receive/handle failure, connection, receipt of response
  }

  /** on selector key callback */
  @Override
  public synchronized void onSelect(SelectionKey key) {
    if (key.isConnectable() && readyState==ReadyState.CONNECTING) {
      if (!headerSent) {
        logger.info("connected (client)");
        try {
          socketChannel.finishConnect();
        }
        catch (Exception e) {
          logger.log(Level.WARNING, "error handling connect", e);
          error();
          return;
        }
        queueHttpRequest();
        headerSent = true;
      }
      else {
        logger.warning("key.isConnectable when CONNECTING but headerSent");
      }
    }
    else
      super.onSelect(key);
  }

  static byte [] END_OF_HEADER = new byte [] { (byte)'\r', (byte)'\n', (byte)'\r', (byte)'\n' };
  @Override
  protected void handleReadQueue() {
    if (!responseReceived) {
      // looking for \r\n\r\n...
      int bufi = 0;
      int bytei = 0;
      int pati = 0;
      ByteBuffer buf = null;
      while (pati < END_OF_HEADER.length) {
        if (buf==null) {
          if (bufi>=readQueue.size()) {
            if (readBuffer!=null) {
              queueReadBuffer();
              continue;
            }
            logger.info("Still waiting for end of header");
            return;
          }         
          buf = readQueue.get(bufi);
          bytei = buf.position();
          bufi++;
        }
        if (bytei>=buf.limit()) {
          buf = null;
          continue;
        }
        if (buf.get(bytei)==END_OF_HEADER[pati]) 
          pati++;
        else
          pati = 0;
        bytei++;
      }
      // got it - stringify
      ByteBuffer bufs [] = new ByteBuffer[bufi];
      int nbytes = 0;
      for (int i=0; i<bufi; i++) {
        bufs[i] = readQueue.removeFirst();
        nbytes += bufs[i].remaining();
      }
      logger.info("Header size "+nbytes+" bytes");
      if (bytei<buf.limit()) {
        // push back rest
        buf.mark();
        buf.position(bytei);
        ByteBuffer nbuf = buf.slice();
        readQueue.add(0, nbuf);
        logger.info("Pushed back remaining "+nbuf.remaining()+" bytes");
        buf.limit(bytei);
        buf.reset();
      }
      Charset charset = Charset.forName("US-ASCII");
      if (charset==null) {
        logger.severe("Charset is null (US-ASCII) - bad!");
      }
      CharsetDecoder cd = charset.newDecoder();
      cd.reset();
      int maxChars = (int)Math.ceil(cd.maxCharsPerByte()*nbytes);
      CharBuffer cb = CharBuffer.allocate(maxChars);
      for (int i =0; i<bufs.length; i++) {
        //logger.info("decode "+bufs[i].remaining()+" bytes from buffer "+i+" position "+bufs[i].position());
        CoderResult cr = cd.decode(bufs[i], cb, (i+1)==bufs.length);
        if (cr!=CoderResult.UNDERFLOW) {
          logger.warning("Error decoding response in buffer "+i+"/"+bufs.length);
          error();
          return;
        }
      }
      cb.limit(cb.position());
      cb.position(0);
      logger.info("Got response, "+cb.limit()+" chars: "+cb.toString());
      // TODO
      
      
      
      responseReceived = true;
      if (readyState==ReadyState.CONNECTING) {
        readyState = ReadyState.OPEN;
        callOnopen();
      }
      else
        logger.warning("Completed header read in state "+readyState);
    }    
  }

  
  private void queueHttpRequest() {
    // TODO Auto-generated method stub
    StringBuilder req = new StringBuilder();
    req.append("GET ");
    String path = url.getPath();
    if (!path.startsWith("/"))
      req.append("/");
    req.append(url.getPath());
    req.append(" HTTP/1.1\r\n");
    req.append("Host: ");
    req.append(url.getHost());
    if (url.getPort()>=0) {
      req.append(":");
      req.append(url.getPort());
    }
    req.append("\r\n");
    req.append("Upgrade: websocket\r\n");
    req.append("Connection: Upgrade\r\n");
    // "a nonce consisting of a randomly selected 16-byte value that has
        // been base64-encoded"
    nonce = new byte[16];
    if (srandom!=null)
      srandom.nextBytes(nonce);
    else
      random.nextBytes(nonce);
    req.append("Sec-WebSocket-Key: ");
    req.append(base64Encode(nonce));
    req.append("\r\n");
    req.append("Sec-WebSocket-Version: 13\r\n");
    req.append("Sec-WebSocket-Protocol: echo-protocol\r\n");
    req.append("\r\n");
    //opt.req.append("Origin: ...\r\n");
    //opt.
    try {
      byte data[] = req.toString().getBytes("US-ASCII");
      queue(data);
      logger.info("Queued request: "+req.toString());
    } catch (Exception e) {
      logger.warning("Error sending request header: "+e);
      error();
    }
  }

  public String base64Encode(byte data[]) {
    return uk.ac.horizon.ubihelper.j2se.Base64.encode(data);
  }
  
  /** test main */
  public static void main(String [] args) {
    if (args.length!=1) {
      logger.severe("Usage: ws://<host>:<port>");
      System.exit(-1);
    }
    try {
      logger.info("Connect to "+args[0]);
      URI url = new URI(args[0]);
      Selector selector = Selector.open();
      ReentrantLock selectorLock = new ReentrantLock();
      ClientWebsocket cw = new ClientWebsocket(url, selector, selectorLock, new WebsocketListener() {
        public void onmessage(Websocket ws, String data) {
          logger.info("onmessage("+data+")");
        }

        public void onerror(Websocket ws) {
          logger.info("onerror()");
        }

        public void onopen(Websocket ws) {
          logger.info("onopen()");
        }

        public void onclose(Websocket ws) {
          logger.info("onclose()");
        }        
      });
      // standard selector
      while (true) {
        selectorLock.lock();
        selectorLock.unlock();
        if (selector.select()!=0) {
          Set<SelectionKey> keys = selector.selectedKeys();
          for (SelectionKey key : keys) {
            if (key.attachment() instanceof SocketChannelWebsocket) {
              SocketChannelWebsocket scw = (SocketChannelWebsocket)key.attachment();
              logger.info("Select for "+key.readyOps());
              scw.onSelect(key);
            }
          }
        }
        
      }
    }
    catch (Exception e) {
      logger.log(Level.SEVERE, "Error: "+e, e);
    }
  }
}




Java Source Code List

org.json.JSONArray.java
org.json.JSONException.java
org.json.JSONObject.java
org.json.JSONString.java
org.json.JSONStringer.java
org.json.JSONTokener.java
org.json.JSONWriter.java
uk.ac.horizon.ubihelper.channel.ChannelFactory.java
uk.ac.horizon.ubihelper.channel.ChannelListener.java
uk.ac.horizon.ubihelper.channel.ChannelManager.java
uk.ac.horizon.ubihelper.channel.ChannelValueEvent.java
uk.ac.horizon.ubihelper.channel.NamedChannel.java
uk.ac.horizon.ubihelper.channel.PullSubscription.java
uk.ac.horizon.ubihelper.channel.SharedVariableChannel.java
uk.ac.horizon.ubihelper.channel.Subscription.java
uk.ac.horizon.ubihelper.dns.DnsClient.java
uk.ac.horizon.ubihelper.dns.DnsProtocol.java
uk.ac.horizon.ubihelper.dns.DnsServer.java
uk.ac.horizon.ubihelper.dns.DnsUtils.java
uk.ac.horizon.ubihelper.httpserver.HttpClientHandler.java
uk.ac.horizon.ubihelper.httpserver.HttpContinuation.java
uk.ac.horizon.ubihelper.httpserver.HttpError.java
uk.ac.horizon.ubihelper.httpserver.HttpListener.java
uk.ac.horizon.ubihelper.j2se.Base64.java
uk.ac.horizon.ubihelper.j2se.Server.java
uk.ac.horizon.ubihelper.net.Fragment.java
uk.ac.horizon.ubihelper.net.Marshaller.java
uk.ac.horizon.ubihelper.net.Message.java
uk.ac.horizon.ubihelper.net.OnPeerConnectionListener.java
uk.ac.horizon.ubihelper.net.PeerConnectionScheduler.java
uk.ac.horizon.ubihelper.net.PeerConnection.java
uk.ac.horizon.ubihelper.protocol.ClientInfo.java
uk.ac.horizon.ubihelper.protocol.ClientState.java
uk.ac.horizon.ubihelper.protocol.MessageUtils.java
uk.ac.horizon.ubihelper.protocol.PeerInfo.java
uk.ac.horizon.ubihelper.protocol.ProtocolManager.java
uk.ac.horizon.ubihelper.service.BroadcastIntentSubscription.java
uk.ac.horizon.ubihelper.service.EnabledPeersChannel.java
uk.ac.horizon.ubihelper.service.LogManager.java
uk.ac.horizon.ubihelper.service.LogSubscription.java
uk.ac.horizon.ubihelper.service.PeerManager.java
uk.ac.horizon.ubihelper.service.PeersOpenHelper.java
uk.ac.horizon.ubihelper.service.Service.java
uk.ac.horizon.ubihelper.service.WifiDiscoveryManager.java
uk.ac.horizon.ubihelper.service.channel.BluetoothDiscoveryChannel.java
uk.ac.horizon.ubihelper.service.channel.CellLocationChannel.java
uk.ac.horizon.ubihelper.service.channel.CellStrengthChannel.java
uk.ac.horizon.ubihelper.service.channel.GpsStatusChannel.java
uk.ac.horizon.ubihelper.service.channel.LocationChannel.java
uk.ac.horizon.ubihelper.service.channel.MicChannel.java
uk.ac.horizon.ubihelper.service.channel.PollingChannel.java
uk.ac.horizon.ubihelper.service.channel.SensorChannel.java
uk.ac.horizon.ubihelper.service.channel.TimeChannel.java
uk.ac.horizon.ubihelper.service.channel.WifiScannerChannel.java
uk.ac.horizon.ubihelper.ui.AboutActivity.java
uk.ac.horizon.ubihelper.ui.ChannelListActivity.java
uk.ac.horizon.ubihelper.ui.ChannelPeerListActivity.java
uk.ac.horizon.ubihelper.ui.ChannelValueActivity.java
uk.ac.horizon.ubihelper.ui.ChannelViewActivity.java
uk.ac.horizon.ubihelper.ui.LoggingChannelListActivity.java
uk.ac.horizon.ubihelper.ui.LoggingPreferences.java
uk.ac.horizon.ubihelper.ui.MainPreferences.java
uk.ac.horizon.ubihelper.ui.ManagePeersActivity.java
uk.ac.horizon.ubihelper.ui.PeerInfoActivity.java
uk.ac.horizon.ubihelper.ui.PeerManualAddActivity.java
uk.ac.horizon.ubihelper.ui.PeerRequestActivity.java
uk.ac.horizon.ubihelper.ui.PeerRequestInfoActivity.java
uk.ac.horizon.ubihelper.ui.SearchPeersActivity.java
uk.ac.horizon.ubihelper.ui.TestActivity.java
uk.ac.horizon.ubihelper.ui.WifiStatusActivity.java
uk.ac.horizon.ubihelper.websocket.ClientWebsocket.java
uk.ac.horizon.ubihelper.websocket.ReadyState.java
uk.ac.horizon.ubihelper.websocket.SocketChannelWebsocket.java
uk.ac.horizon.ubihelper.websocket.WebsocketListener.java
uk.ac.horizon.ubihelper.websocket.Websocket.java