com.neovisionaries.ws.client.WebSocketInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nv-websocket-client Show documentation
Show all versions of nv-websocket-client Show documentation
WebSocket client implementation in Java.
/*
* Copyright (C) 2015-2016 Neo Visionaries Inc.
*
* 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 com.neovisionaries.ws.client;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
class WebSocketInputStream extends FilterInputStream
{
public WebSocketInputStream(InputStream in)
{
super(in);
}
public String readLine() throws IOException
{
return Misc.readLine(this, "UTF-8");
}
public WebSocketFrame readFrame() throws IOException, WebSocketException
{
// Buffer.
byte[] buffer = new byte[8];
try
{
// Read the first two bytes.
readBytes(buffer, 2);
}
catch (InsufficientDataException e)
{
if (e.getReadByteCount() == 0)
{
// The connection has been closed without receiving a close frame.
// Strictly speaking, this is a violation against RFC 6455.
throw new NoMoreFrameException();
}
else
{
// Re-throw the exception.
throw e;
}
}
// FIN
boolean fin = ((buffer[0] & 0x80) != 0);
// RSV1, RSV2, RSV3
boolean rsv1 = ((buffer[0] & 0x40) != 0);
boolean rsv2 = ((buffer[0] & 0x20) != 0);
boolean rsv3 = ((buffer[0] & 0x10) != 0);
// Opcode
int opcode = (buffer[0] & 0x0F);
// Mask flag. This should never be true because the specification
// (RFC 6455, 5. Data Framing, 5.1. Overview) says as follows:
//
// A server MUST NOT mask any frames that it sends to the client.
//
boolean mask = ((buffer[1] & 0x80) != 0);
// The payload length. It is expressed in 7 bits.
long payloadLength = buffer[1] & 0x7F;
if (payloadLength == 126)
{
// Read the extended payload length.
// It is expressed in 2 bytes in network byte order.
readBytes(buffer, 2);
// Interpret the bytes as a number.
payloadLength = (((buffer[0] & 0xFF) << 8) |
((buffer[1] & 0xFF) ));
}
else if (payloadLength == 127)
{
// Read the extended payload length.
// It is expressed in 8 bytes in network byte order.
readBytes(buffer, 8);
// From RFC 6455, p29.
//
// the most significant bit MUST be 0
//
if ((buffer[0] & 0x80) != 0)
{
// The payload length in a frame is invalid.
throw new WebSocketException(
WebSocketError.INVALID_PAYLOAD_LENGTH,
"The payload length of a frame is invalid.");
}
// Interpret the bytes as a number.
payloadLength = (((buffer[0] & 0xFF) << 56) |
((buffer[1] & 0xFF) << 48) |
((buffer[2] & 0xFF) << 40) |
((buffer[3] & 0xFF) << 32) |
((buffer[4] & 0xFF) << 24) |
((buffer[5] & 0xFF) << 16) |
((buffer[6] & 0xFF) << 8) |
((buffer[7] & 0xFF) ));
}
// Masking key
byte[] maskingKey = null;
if (mask)
{
// Read the masking key. (This should never happen.)
maskingKey = new byte[4];
readBytes(maskingKey, 4);
}
if (Integer.MAX_VALUE < payloadLength)
{
// In Java, the maximum array size is Integer.MAX_VALUE.
// Skip the payload and raise an exception.
skipQuietly(payloadLength);
throw new WebSocketException(
WebSocketError.TOO_LONG_PAYLOAD,
"The payload length of a frame exceeds the maximum array size in Java.");
}
// Read the payload if the payload length is not 0.
byte[] payload = readPayload(payloadLength, mask, maskingKey);
// Create a WebSocketFrame instance that represents a frame.
return new WebSocketFrame()
.setFin(fin)
.setRsv1(rsv1)
.setRsv2(rsv2)
.setRsv3(rsv3)
.setOpcode(opcode)
.setMask(mask)
.setPayload(payload);
}
void readBytes(byte[] buffer, int length) throws IOException, WebSocketException
{
// Read
int total = 0;
while (total < length)
{
int count = read(buffer, total, length - total);
if (count <= 0)
{
// The end of the stream has been reached unexpectedly.
throw new InsufficientDataException(length, total);
}
total += count;
}
}
private void skipQuietly(long length)
{
try
{
skip(length);
}
catch (IOException e)
{
}
}
private byte[] readPayload(long payloadLength, boolean mask, byte[] maskingKey) throws IOException, WebSocketException
{
if (payloadLength == 0)
{
return null;
}
byte[] payload;
try
{
// Allocate a memory area to hold the content of the payload.
payload = new byte[(int)payloadLength];
}
catch (OutOfMemoryError e)
{
// OutOfMemoryError occurred during a trial to allocate a memory area
// for a frame's payload. Skip the payload and raise an exception.
skipQuietly(payloadLength);
throw new WebSocketException(
WebSocketError.INSUFFICIENT_MEMORY_FOR_PAYLOAD,
"OutOfMemoryError occurred during a trial to allocate a memory area for a frame's payload: " + e.getMessage(), e);
}
// Read the payload.
readBytes(payload, payload.length);
// If masked.
if (mask)
{
// Unmasked the payload.
WebSocketFrame.mask(maskingKey, payload);
}
return payload;
}
}