package jxm.JWS.DF; import jxm.CHRF; import jxm.JWS.FR.CloseFrame; import jxm.JWS.FR.FramedataImpl1; import jxm.JWS.EX.*; import jxm.JWS.FR.FrameBuilder; import jxm.JWS.FR.Framedata; import jxm.JWS.FR.Framedata.Opcode; import jxm.JWS.HS.*; import java.nio.ByteBuffer; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Random; public class Draft_75 extends Draft { /** * The byte representing CR, or Carriage Return, or \r */ public static final byte CR = (byte) 0x0D; /** * The byte representing LF, or Line Feed, or \n */ public static final byte LF = (byte) 0x0A; /** * The byte representing the beginning of a WebSocket text frame. */ public static final byte START_OF_FRAME = (byte) 0x00; /** * The byte representing the end of a WebSocket text frame. */ public static final byte END_OF_FRAME = (byte) 0xFF; /** * Is only used to detect protocol violations */ protected boolean readingState = false; protected List<Framedata> readyframes = new LinkedList<Framedata>(); protected ByteBuffer currentFrame; private final Random reuseableRandom = new Random(); @Override public HandshakeState acceptHandshakeAsClient(ClientHandshake request, ServerHandshake response) { return request.getFieldValue("WebSocket-Origin").equals(response.getFieldValue("Origin")) && basicAccept(response) ? HandshakeState.MATCHED : HandshakeState.NOT_MATCHED; } @Override public HandshakeState acceptHandshakeAsServer(ClientHandshake handshakedata) { if (handshakedata.hasFieldValue("Origin") && basicAccept(handshakedata)) { return HandshakeState.MATCHED; } return HandshakeState.NOT_MATCHED; } @Override public ByteBuffer createBinaryFrame(Framedata framedata) { if (framedata.getOpcode() != Opcode.TEXT) { throw new RuntimeException("only text frames supported"); } ByteBuffer pay = framedata.getPayloadData(); ByteBuffer b = ByteBuffer.allocate(pay.remaining() + 2); b.put(START_OF_FRAME); pay.mark(); b.put(pay); pay.reset(); b.put(END_OF_FRAME); b.flip(); return b; } @Override public List<Framedata> createFrames(ByteBuffer binary, boolean mask) { throw new RuntimeException("not yet implemented"); } @Override public List<Framedata> createFrames(String text, boolean mask) { FrameBuilder frame = new FramedataImpl1(); try { frame.setPayload(ByteBuffer.wrap(CHRF.utf8Bytes(text))); } catch (InvalidDataException e) { throw new NotSendableException(e); } frame.setFin(true); frame.setOptcode(Opcode.TEXT); frame.setTransferemasked(mask); return Collections.singletonList((Framedata) frame); } @Override public ClientHandshakeBuilder postProcessHandshakeRequestAsClient(ClientHandshakeBuilder request) throws InvalidHandshakeException { request.put("Upgrade", "WebSocket"); request.put("Connection", "Upgrade"); if (!request.hasFieldValue("Origin")) { request.put("Origin", "random" + reuseableRandom.nextInt()); } return request; } @Override public HandshakeBuilder postProcessHandshakeResponseAsServer(ClientHandshake request, ServerHandshakeBuilder response) throws InvalidHandshakeException { response.setHttpStatusMessage("Web Socket Protocol Handshake"); response.put("Upgrade", "WebSocket"); response.put("Connection", request.getFieldValue("Connection")); // to respond to a Connection keep alive response.put("WebSocket-Origin", request.getFieldValue("Origin")); String location = "ws://" + request.getFieldValue("Host") + request.getResourceDescriptor(); response.put("WebSocket-Location", location); // TODO handle Sec-WebSocket-Protocol and Set-Cookie return response; } protected List<Framedata> translateRegularFrame(ByteBuffer buffer) throws InvalidDataException { while (buffer.hasRemaining()) { byte newestByte = buffer.get(); if (newestByte == START_OF_FRAME) { // Beginning of Frame if (readingState) throw new InvalidFrameException("unexpected START_OF_FRAME"); readingState = true; } else if (newestByte == END_OF_FRAME) { // End of Frame if (!readingState) throw new InvalidFrameException("unexpected END_OF_FRAME"); // currentFrame will be null if END_OF_FRAME was send directly after // START_OF_FRAME, thus we will send 'null' as the sent message. if (this.currentFrame != null) { currentFrame.flip(); FramedataImpl1 curframe = new FramedataImpl1(); curframe.setPayload(currentFrame); curframe.setFin(true); curframe.setOptcode(Opcode.TEXT); readyframes.add(curframe); this.currentFrame = null; buffer.mark(); } readingState = false; } else if (readingState) { // Regular frame data, add to current frame buffer //TODO This code is very expensive and slow if (currentFrame == null) { currentFrame = createBuffer(); } else if (!currentFrame.hasRemaining()) { currentFrame = increaseBuffer(currentFrame); } currentFrame.put(newestByte); } else { return null; } } // if no error occurred this block will be reached /*if( readingState ) { checkAlloc(currentFrame.position()+1); }*/ List<Framedata> frames = readyframes; readyframes = new LinkedList<Framedata>(); return frames; } @Override public List<Framedata> translateFrame(ByteBuffer buffer) throws InvalidDataException { List<Framedata> frames = translateRegularFrame(buffer); if (frames == null) { throw new InvalidDataException(CloseFrame.PROTOCOL_ERROR); } return frames; } @Override public void reset() { readingState = false; this.currentFrame = null; } @Override public CloseHandshakeType getCloseHandshakeType() { return CloseHandshakeType.NONE; } public ByteBuffer createBuffer() { return ByteBuffer.allocate(INITIAL_FAMESIZE); } public ByteBuffer increaseBuffer(ByteBuffer full) throws LimitExedeedException, InvalidDataException { full.flip(); ByteBuffer newbuffer = ByteBuffer.allocate(checkAlloc(full.capacity() * 2)); newbuffer.put(full); return newbuffer; } @Override public Draft copyInstance() { return new Draft_75(); } }