/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.remote.websocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import com.caucho.util.L10N;
/**
* WebSocketReader reads a single WebSocket packet.
*
* <code><pre>
* 0x00 utf-8 data 0xff
* </pre></code>
*/
public class WebSocketReader extends Reader
implements WebSocketConstants
{
private static final L10N L = new L10N(WebSocketReader.class);
private FrameInputStream _is;
private boolean _isFinal;
private long _length;
public WebSocketReader(FrameInputStream is)
throws IOException
{
_is = is;
}
public void init()
throws IOException
{
_isFinal = _is.isFinal();
_length = _is.getLength();
}
public long getLength()
{
return _length;
}
@Override
public int read()
throws IOException
{
int d1 = readByte();
if (d1 < 0x80)
return d1;
if ((d1 & 0xe0) == 0xc0) {
int d2 = readByte();
return ((d1 & 0x1f) << 6) + (d2 & 0x3f);
}
else {
int d2 = readByte();
int d3 = readByte();
return ((d2 & 0xf) << 12) + ((d2 & 0x3f) << 6) + (d3 & 0x3f);
}
}
@Override
public int read(char []buffer, int offset, int length)
throws IOException
{
int i = 0;
int d1;
while (length-- > 0 && (d1 = readByte()) >= 0) {
char ch;
if (d1 < 0x80) {
ch = (char) d1;
}
else if ((d1 & 0xe0) == 0xc0) {
int d2 = readByte();
ch = (char) (((d1 & 0x1f) << 6) + (d2 & 0x3f));
}
else {
int d2 = readByte();
int d3 = readByte();
ch = (char) (((d2 & 0xf) << 12) + ((d2 & 0x3f) << 6) + (d3 & 0x3f));
}
buffer[offset + i++] = ch;
}
if (i == 0)
return -1;
else
return i;
}
private int readByte()
throws IOException
{
FrameInputStream is = _is;
while (_length == 0 && ! _isFinal) {
if (! is.readFrameHeader())
return -1;
_isFinal = is.isFinal();
_length = is.getLength();
}
if (_length > 0) {
int ch = is.read();
_length--;
return ch;
}
else
return -1;
}
private void readFrameHeader()
throws IOException
{
FrameInputStream is = _is;
if (_isFinal || _length > 0)
throw new IllegalStateException();
while (! _isFinal && _length == 0) {
int frame1 = is.read();
int frame2 = is.read();
boolean isFinal = (frame1 & FLAG_FIN) == FLAG_FIN;
int op = frame1 & 0xf;
if (op != OP_CONT) {
throw new IOException(L.l("{0}: expected op=CONT '0x{1}' because WebSocket binary protocol expects 0x80 at beginning",
this, Integer.toHexString(frame1 & 0xffff)));
}
_isFinal = isFinal;
long length = frame2 & 0x7f;
if (length < 0x7e) {
}
else if (length == 0x7e) {
length = ((((long) is.read()) << 8)
+ (((long) is.read())));
}
else {
length = ((((long) is.read()) << 56)
+ (((long) is.read()) << 48)
+ (((long) is.read()) << 40)
+ (((long) is.read()) << 32)
+ (((long) is.read()) << 24)
+ (((long) is.read()) << 16)
+ (((long) is.read()) << 8)
+ (((long) is.read())));
}
_length = length;
}
}
@Override
public void close()
throws IOException
{
while (_length > 0 && !_isFinal) {
skip(_length);
}
}
}