package org.jboss.pitbull.internal.client.websocket.protocol.ietf00;
import org.jboss.pitbull.StatusCode;
import org.jboss.pitbull.client.ClientInvocation;
import org.jboss.pitbull.client.HandshakeFailure;
import org.jboss.pitbull.client.HttpConnectionFactory;
import org.jboss.pitbull.client.WebSocketBuilder;
import org.jboss.pitbull.internal.NotImplementedYetException;
import org.jboss.pitbull.internal.client.ClientConnectionImpl;
import org.jboss.pitbull.internal.client.ClientResponseImpl;
import org.jboss.pitbull.internal.nio.socket.BufferedBlockingInputStream;
import org.jboss.pitbull.internal.nio.socket.BufferedBlockingOutputStream;
import org.jboss.pitbull.internal.nio.websocket.WebSocketImpl;
import org.jboss.pitbull.internal.nio.websocket.impl.oio.ClosingStrategy;
import org.jboss.pitbull.internal.nio.websocket.impl.oio.OioWebSocket;
import org.jboss.pitbull.internal.nio.websocket.impl.oio.internal.protocol.ietf00.Hybi00Handshake;
import org.jboss.pitbull.internal.nio.websocket.impl.oio.internal.util.Hash;
import org.jboss.pitbull.websocket.WebSocket;
import java.io.IOException;
import java.util.Arrays;
import java.util.Random;
import static org.jboss.pitbull.internal.nio.websocket.impl.oio.internal.WebSocketHeaders.*;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class Hybi00WebSocketBuilder extends WebSocketBuilder
{
protected Random random = new Random();
protected String key()
{
int spaces_1 = Hash.nextInt(12) + 1;
int max_1 = Integer.MAX_VALUE / spaces_1;
int number_1 = Hash.nextInt(max_1);
int product_1 = number_1 * spaces_1;
String key_1 = Integer.toString(product_1);
int randomCharRange = 0x2F - 0x21 + 0x7E - 0x3A;
int numChars = Hash.nextInt(12) + 1;
StringBuilder builder = new StringBuilder(key_1);
for (int i = 0; i < numChars; i++)
{
int index = Hash.nextInt(builder.length());
int randomChar = Hash.nextInt(randomCharRange);
char c = 0;
if (randomChar < (0x2F - 0x21))
{
c = (char) (0x21 + randomChar);
}
else
{
c = (char) (0x3A + randomChar);
}
builder.insert(index, c);
}
for (int i = 0; i < spaces_1; i++)
{
int index = Hash.nextInt(builder.length());
index--;
if (index < 1) index = 1;
builder.insert(index, ' ');
}
return builder.toString();
}
@Override
protected WebSocket doConnect() throws IOException
{
final ClientConnectionImpl connection;
if (secured)
{
if (trustStore == null) connection = (ClientConnectionImpl) HttpConnectionFactory.https(host, port);
else throw new NotImplementedYetException("wss with truststore not implemented yet");
}
else
{
connection = (ClientConnectionImpl) HttpConnectionFactory.http(host, port);
}
try
{
ClientInvocation invocation = connection.request(path).get();
invocation.header("Upgrade", "WebSocket");
invocation.header("Connection", "Upgrade");
if (origin != null) invocation.header(SEC_WEBSOCKET_ORIGIN.getCanonicalHeaderName(), origin);
if (protocol != null) invocation.header(SEC_WEBSOCKET_PROTOCOL.getCanonicalHeaderName(), protocol);
String key_1 = key();
invocation.header(SEC_WEBSOCKET_KEY1.getCanonicalHeaderName(), key_1);
String key_2 = key();
invocation.header(SEC_WEBSOCKET_KEY2.getCanonicalHeaderName(), key_2);
byte[] key3 = new byte[8];
Hash.getRandomBytes(key3);
invocation.getRequestBody().write(key3);
ClientResponseImpl response = (ClientResponseImpl) invocation.invoke();
if (response.getStatus() != StatusCode.SWITCHING_PROTOCOLS)
{
throw new HandshakeFailure("Error making handshake: " + response.getStatus(), response);
}
String upgrade = response.getHeaders().getFirstHeader("Upgrade");
if (!"websocket".equalsIgnoreCase(upgrade))
{
throw new HandshakeFailure("Incorrect Upgrade heaader", response);
}
if (protocol != null)
{
String chosenProtocol = response.getHeaders().getFirstHeader(SEC_WEBSOCKET_PROTOCOL.getCanonicalHeaderName());
boolean match = false;
String[] split = protocol.split(",");
for (String p : split)
{
if (p.equals(chosenProtocol))
{
match = true;
break;
}
}
if (!match)
{
throw new HandshakeFailure("No protocol match", response);
}
}
if (origin != null)
{
String chosenOrigin = response.getHeaders().getFirstHeader(SEC_WEBSOCKET_ORIGIN.getCanonicalHeaderName());
if (!origin.equals(chosenOrigin))
{
throw new HandshakeFailure("No origin match", response);
}
}
BufferedBlockingInputStream inputStream = new BufferedBlockingInputStream(connection.getChannel(), response.getBuffer());
Hybi00Handshake hybi00Handshake = new Hybi00Handshake();
final byte[] sentSolution = Hybi00Handshake.solve(hybi00Handshake.getHashAlgorithm(), key_1, key_2, key3);
final byte[] receivedSolution = new byte[16];
for (int i = 0; i < 16; i++)
{
int b = inputStream.read();
receivedSolution[i] = (byte) b;
}
if (!Arrays.equals(sentSolution, receivedSolution))
{
throw new HandshakeFailure("Keys don't match");
}
BufferedBlockingOutputStream outputStream = new BufferedBlockingOutputStream((connection.getChannel()));
OioWebSocket oioWebSocket = hybi00Handshake.getClientWebSocket(
uri,
inputStream,
outputStream,
new ClosingStrategy()
{
@Override
public void doClose() throws IOException
{
connection.close();
}
}
);
return new WebSocketImpl(connection, oioWebSocket);
}
catch (IOException e)
{
connection.close();
throw e;
}
catch (RuntimeException e)
{
connection.close();
throw e;
}
}
}