package cz.woitee.websockets.streams;
import java.io.*;
import java.net.Socket;
import cz.woitee.websockets.WebSocketFrame;
import cz.woitee.websockets.WebSocketException;
public class ServersideWebSocketInputStream extends FilterInputStream {
protected Socket socket;
public ServersideWebSocketInputStream(Socket socket) throws IOException {
super(socket.getInputStream());
this.socket = socket;
}
private byte[] payload = new byte[0];
private int ptr = 0;
/**
* Reads next usable WebSocket frame and saves the bytes in the payload variable.
* Ping frames will be automatically responded to along the way and not returned.
* Masking on client messages is required, so non-masked frames will cause a
* WebSocketException.
* Close frames will cause a WebSocketException.
*
* @return Bytes stored in the payload variable, or -1 if no bytes could be read.
* @throws WebSocketException
* @throws IOException
*/
private int readNextFrame() throws WebSocketException, IOException {
boolean ping;
WebSocketFrame readResult;
do {
readResult = WebSocketFrame.readFrame(in);
if (readResult.bytesRead == -1) { return -1; }
if (readResult.masked == false) {
throw new WebSocketException("Received an unmasked frame from client.");
}
if (readResult.opcode == 0x8) {
throw new WebSocketException("The opposite side closed the WebSocket connection.");
}
ping = readResult.opcode == 0x9;
if (ping) {
//automatically form ping response (pong)
byte[] appData = readResult.bytes;
socket.getOutputStream().write(
WebSocketFrame.construct(appData, 0, appData.length, 0xA)
);
}
} while (ping);
//readResult.bytesRead != -1, so the read was succesful
payload = readResult.bytes;
ptr = 0;
return payload.length;
}
@Override
public int read() throws IOException {
while (ptr >= payload.length) {
if (readNextFrame() == -1) { return -1; }
}
return payload[ptr++];
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) { return 0; }
//try copying at least one byte
int res = read();
if (res == -1) { return -1; }
b[off] = (byte)res;
//copy as many more as you can without blocking
int copyPtr = off + 1;
while((in.available() > 0 || ptr < payload.length) && copyPtr < off+len) {
if (ptr < payload.length) {
b[copyPtr++] = payload[ptr++];
} else {
if (readNextFrame() == -1) { return -1; }
}
}
return copyPtr - off;
}
}