427690 - Remove Mux Extension and related support.

This commit is contained in:
Simone Bordet 2014-02-10 13:10:58 +01:00
parent 66a19bd646
commit b37c68db3b
56 changed files with 0 additions and 5036 deletions

View File

@ -19,7 +19,6 @@
<module>websocket-client</module>
<module>websocket-server</module>
<module>websocket-servlet</module>
<module>websocket-mux-extension</module>
<module>javax-websocket-client-impl</module>
<module>javax-websocket-server-impl</module>
</modules>

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-parent</artifactId>
<version>9.1.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>websocket-mux-extension</artifactId>
<name>Jetty :: Websocket :: Mux Extension</name>
<properties>
<bundle-symbolic-name>${project.groupId}.mux</bundle-symbolic-name>
</properties>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>tests-jar</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,65 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.extensions.AbstractExtension;
/**
* Multiplexing Extension for WebSockets.
* <p>
* Supporting <a href="https://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-08">draft-ietf-hybi-websocket-multiplexing-08</a> Specification.
*/
public abstract class AbstractMuxExtension extends AbstractExtension
{
private Muxer muxer;
public AbstractMuxExtension()
{
super();
}
public abstract void configureMuxer(Muxer muxer);
@Override
public void incomingFrame(Frame frame)
{
this.muxer.incomingFrame(frame);
}
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
/* do nothing here, allow Muxer to handle this aspect */
}
@Override
public void setConnection(LogicalConnection connection)
{
super.setConnection(connection);
if (muxer != null)
{
throw new RuntimeException("Cannot reset muxer physical connection once established");
}
this.muxer = new Muxer(connection);
configureMuxer(this.muxer);
}
}

View File

@ -1,248 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.ConnectionState;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.io.IOState;
import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
/**
* MuxChannel, acts as WebSocketConnection for specific sub-channel.
*/
public class MuxChannel implements LogicalConnection, IncomingFrames, SuspendToken, ConnectionStateListener
{
private final long channelId;
private final Muxer muxer;
private final AtomicBoolean suspendToken;
private IOState ioState;
private WebSocketPolicy policy;
private WebSocketSession session;
private IncomingFrames incoming;
public MuxChannel(long channelId, Muxer muxer)
{
this.channelId = channelId;
this.muxer = muxer;
this.policy = muxer.getPolicy().clonePolicy();
this.suspendToken = new AtomicBoolean(false);
this.ioState = new IOState();
this.ioState.addListener(this);
}
@Override
public Executor getExecutor()
{
// TODO Auto-generated method stub
return null;
}
@Override
public void close()
{
close(StatusCode.NORMAL,null);
}
@Override
public void close(int statusCode, String reason)
{
CloseInfo close = new CloseInfo(statusCode,reason);
// TODO: disconnect callback?
outgoingFrame(close.asFrame(),null);
}
@Override
public void disconnect()
{
// TODO: disconnect the virtual end-point?
}
@Override
public ByteBufferPool getBufferPool()
{
// TODO Auto-generated method stub
return null;
}
public long getChannelId()
{
return channelId;
}
@Override
public long getIdleTimeout()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public IOState getIOState()
{
// TODO Auto-generated method stub
return null;
}
@Override
public InetSocketAddress getLocalAddress()
{
// TODO Auto-generated method stub
return null;
}
@Override
public long getMaxIdleTimeout()
{
// TODO Auto-generated method stub
return 0;
}
@Override
public WebSocketPolicy getPolicy()
{
return policy;
}
@Override
public InetSocketAddress getRemoteAddress()
{
return muxer.getRemoteAddress();
}
@Override
public WebSocketSession getSession()
{
return session;
}
/**
* Incoming exceptions from Muxer.
*/
@Override
public void incomingError(Throwable e)
{
incoming.incomingError(e);
}
/**
* Incoming frames from Muxer
*/
@Override
public void incomingFrame(Frame frame)
{
incoming.incomingFrame(frame);
}
public boolean isActive()
{
return (ioState.isOpen());
}
@Override
public boolean isOpen()
{
return isActive() && muxer.isOpen();
}
@Override
public boolean isReading()
{
return true;
}
public void onClose()
{
}
@Override
public void onConnectionStateChange(ConnectionState state)
{
// TODO Auto-generated method stub
}
public void onOpen()
{
this.ioState.onOpened();
}
/**
* Frames destined for the Muxer
*/
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
muxer.output(channelId,frame,callback);
}
@Override
public void resume()
{
if (suspendToken.getAndSet(false))
{
// TODO: Start reading again. (how?)
}
}
@Override
public void setMaxIdleTimeout(long ms)
{
// TODO Auto-generated method stub
}
@Override
public void setNextIncomingFrames(IncomingFrames incoming)
{
this.incoming = incoming;
}
@Override
public void setSession(WebSocketSession session)
{
this.session = session;
// session.setOutgoing(this);
}
public void setSubProtocol(String subProtocol)
{
}
@Override
public SuspendToken suspend()
{
suspendToken.set(true);
// TODO: how to suspend reading?
return this;
}
}

View File

@ -1,24 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
public interface MuxControlBlock
{
public int getOpCode();
}

View File

@ -1,40 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.api.WebSocketException;
@SuppressWarnings("serial")
public class MuxException extends WebSocketException
{
public MuxException(String message)
{
super(message);
}
public MuxException(String message, Throwable cause)
{
super(message,cause);
}
public MuxException(Throwable cause)
{
super(cause);
}
}

View File

@ -1,272 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.ArrayByteBufferPool;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.frames.BinaryFrame;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.eclipse.jetty.websocket.mux.op.MuxDropChannel;
import org.eclipse.jetty.websocket.mux.op.MuxFlowControl;
import org.eclipse.jetty.websocket.mux.op.MuxNewChannelSlot;
/**
* Generate Mux frames destined for the physical connection.
*/
public class MuxGenerator
{
private static final int CONTROL_BUFFER_SIZE = 2 * 1024;
/** 4 bytes for channel ID + 1 for fin/rsv/opcode */
private static final int DATA_FRAME_OVERHEAD = 5;
private ByteBufferPool bufferPool;
private OutgoingFrames outgoing;
public MuxGenerator()
{
this(new ArrayByteBufferPool());
}
public MuxGenerator(ByteBufferPool bufferPool)
{
this.bufferPool = bufferPool;
}
public void generate(long channelId, Frame frame, WriteCallback callback)
{
ByteBuffer muxPayload = bufferPool.acquire(frame.getPayloadLength() + DATA_FRAME_OVERHEAD,false);
BufferUtil.flipToFill(muxPayload);
// start building mux payload
writeChannelId(muxPayload,channelId);
byte b = (byte)(frame.isFin()?0x80:0x00); // fin
b |= (byte)(frame.isRsv1()?0x40:0x00); // rsv1
b |= (byte)(frame.isRsv2()?0x20:0x00); // rsv2
b |= (byte)(frame.isRsv3()?0x10:0x00); // rsv3
b |= (byte)(frame.getOpCode() & 0x0F); // opcode
muxPayload.put(b);
BufferUtil.put(frame.getPayload(),muxPayload);
// build muxed frame
WebSocketFrame muxFrame = new BinaryFrame();
BufferUtil.flipToFlush(muxPayload,0);
muxFrame.setPayload(muxPayload);
// NOTE: the physical connection will handle masking rules for this frame.
// release original buffer (no longer needed)
bufferPool.release(frame.getPayload());
// send muxed frame down to the physical connection.
outgoing.outgoingFrame(muxFrame,callback);
}
public void generate(WriteCallback callback,MuxControlBlock... blocks) throws IOException
{
if ((blocks == null) || (blocks.length <= 0))
{
return; // nothing to do
}
ByteBuffer payload = bufferPool.acquire(CONTROL_BUFFER_SIZE,false);
BufferUtil.flipToFill(payload);
writeChannelId(payload,0); // control channel
for (MuxControlBlock block : blocks)
{
switch (block.getOpCode())
{
case MuxOp.ADD_CHANNEL_REQUEST:
{
MuxAddChannelRequest op = (MuxAddChannelRequest)block;
byte b = (byte)((op.getOpCode() & 0x07) << 5); // opcode
b |= (byte)((op.getRsv() & 0x07) << 2); // rsv
b |= (op.getEncoding() & 0x03); // enc
payload.put(b); // opcode + rsv + enc
writeChannelId(payload,op.getChannelId());
write139Buffer(payload,op.getHandshake());
break;
}
case MuxOp.ADD_CHANNEL_RESPONSE:
{
MuxAddChannelResponse op = (MuxAddChannelResponse)block;
byte b = (byte)((op.getOpCode() & 0x07) << 5); // opcode
b |= (op.isFailed()?0x10:0x00); // failure bit
b |= (byte)((op.getRsv() & 0x03) << 2); // rsv
b |= (op.getEncoding() & 0x03); // enc
payload.put(b); // opcode + f + rsv + enc
writeChannelId(payload,op.getChannelId());
if (op.getHandshake() != null)
{
write139Buffer(payload,op.getHandshake());
}
else
{
// no handshake details
write139Size(payload,0);
}
break;
}
case MuxOp.DROP_CHANNEL:
{
MuxDropChannel op = (MuxDropChannel)block;
byte b = (byte)((op.getOpCode() & 0x07) << 5); // opcode
b |= (byte)(op.getRsv() & 0x1F); // rsv
payload.put(b); // opcode + rsv
writeChannelId(payload,op.getChannelId());
write139Buffer(payload,op.asReasonBuffer());
break;
}
case MuxOp.FLOW_CONTROL:
{
MuxFlowControl op = (MuxFlowControl)block;
byte b = (byte)((op.getOpCode() & 0x07) << 5); // opcode
b |= (byte)(op.getRsv() & 0x1F); // rsv
payload.put(b); // opcode + rsv
writeChannelId(payload,op.getChannelId());
write139Size(payload,op.getSendQuotaSize());
break;
}
case MuxOp.NEW_CHANNEL_SLOT:
{
MuxNewChannelSlot op = (MuxNewChannelSlot)block;
byte b = (byte)((op.getOpCode() & 0x07) << 5); // opcode
b |= (byte)(op.getRsv() & 0x0F) << 1; // rsv
b |= (byte)(op.isFallback()?0x01:0x00); // fallback bit
payload.put(b); // opcode + rsv + fallback bit
write139Size(payload,op.getNumberOfSlots());
write139Size(payload,op.getInitialSendQuota());
break;
}
}
}
BufferUtil.flipToFlush(payload,0);
WebSocketFrame frame = new BinaryFrame();
frame.setPayload(payload);
outgoing.outgoingFrame(frame,callback);
}
public OutgoingFrames getOutgoing()
{
return outgoing;
}
public void setOutgoing(OutgoingFrames outgoing)
{
this.outgoing = outgoing;
}
/**
* Write a 1/3/9 encoded size, then a byte buffer of that size.
*
* @param payload
* @param buffer
*/
public void write139Buffer(ByteBuffer payload, ByteBuffer buffer)
{
write139Size(payload,buffer.remaining());
writeBuffer(payload,buffer);
}
/**
* Write a 1/3/9 encoded size.
*
* @param payload
* @param size
*/
public void write139Size(ByteBuffer payload, long size)
{
if (size > 0xFF_FF)
{
// 9 byte encoded
payload.put((byte)0x7F);
payload.putLong(size);
return;
}
if (size >= 0x7E)
{
// 3 byte encoded
payload.put((byte)0x7E);
payload.put((byte)(size >> 8));
payload.put((byte)(size & 0xFF));
return;
}
// 1 byte (7 bit) encoded
payload.put((byte)(size & 0x7F));
}
public void writeBuffer(ByteBuffer payload, ByteBuffer buffer)
{
BufferUtil.put(buffer,payload);
}
/**
* Write multiplexing channel id, using logical channel id encoding (of 1,2,3, or 4 octets)
*
* @param payload
* @param channelId
*/
public void writeChannelId(ByteBuffer payload, long channelId)
{
if (channelId > 0x1F_FF_FF_FF)
{
throw new MuxException("Illegal Channel ID: too big");
}
if (channelId > 0x1F_FF_FF)
{
// 29 bit channel id (4 bytes)
payload.put((byte)(0xE0 | ((channelId >> 24) & 0x1F)));
payload.put((byte)((channelId >> 16) & 0xFF));
payload.put((byte)((channelId >> 8) & 0xFF));
payload.put((byte)(channelId & 0xFF));
return;
}
if (channelId > 0x3F_FF)
{
// 21 bit channel id (3 bytes)
payload.put((byte)(0xC0 | ((channelId >> 16) & 0x1F)));
payload.put((byte)((channelId >> 8) & 0xFF));
payload.put((byte)(channelId & 0xFF));
return;
}
if (channelId > 0x7F)
{
// 14 bit channel id (2 bytes)
payload.put((byte)(0x80 | ((channelId >> 8) & 0x3F)));
payload.put((byte)(channelId & 0xFF));
return;
}
// 7 bit channel id
payload.put((byte)(channelId & 0x7F));
}
}

View File

@ -1,28 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
public final class MuxOp
{
public static final byte ADD_CHANNEL_REQUEST = 0;
public static final byte ADD_CHANNEL_RESPONSE = 1;
public static final byte FLOW_CONTROL = 2;
public static final byte DROP_CHANNEL = 3;
public static final byte NEW_CHANNEL_SLOT = 4;
}

View File

@ -1,410 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.eclipse.jetty.websocket.mux.op.MuxDropChannel;
import org.eclipse.jetty.websocket.mux.op.MuxFlowControl;
import org.eclipse.jetty.websocket.mux.op.MuxNewChannelSlot;
public class MuxParser
{
public static interface Listener
{
public void onMuxAddChannelRequest(MuxAddChannelRequest request);
public void onMuxAddChannelResponse(MuxAddChannelResponse response);
public void onMuxDropChannel(MuxDropChannel drop);
public void onMuxedFrame(MuxedFrame frame);
public void onMuxException(MuxException e);
public void onMuxFlowControl(MuxFlowControl flow);
public void onMuxNewChannelSlot(MuxNewChannelSlot slot);
}
private final static Logger LOG = Log.getLogger(MuxParser.class);
private MuxedFrame muxframe = new MuxedFrame();
private MuxParser.Listener events;
private long channelId;
public MuxParser.Listener getEvents()
{
return events;
}
/**
* Parse the raw {@link WebSocketFrame} payload data for various Mux frames.
*
* @param frame
* the WebSocketFrame to parse for mux payload
*/
public synchronized void parse(Frame frame)
{
if (events == null)
{
throw new RuntimeException("No " + MuxParser.Listener.class + " specified");
}
if (!frame.hasPayload())
{
LOG.debug("No payload data, skipping");
return; // nothing to parse
}
if (frame.getOpCode() != OpCode.BINARY)
{
LOG.debug("Not a binary opcode (base frame), skipping");
return; // not a binary opcode
}
LOG.debug("Parsing Mux Payload of {}",frame);
try
{
ByteBuffer buffer = frame.getPayload().slice();
if (buffer.remaining() <= 0)
{
return;
}
if (frame.getOpCode() == OpCode.CONTINUATION)
{
muxframe.reset();
muxframe.setFin(frame.isFin());
muxframe.setFin(frame.isRsv1());
muxframe.setFin(frame.isRsv2());
muxframe.setFin(frame.isRsv3());
muxframe.setIsContinuation();
parseDataFramePayload(buffer);
}
else
{
// new frame
channelId = readChannelId(buffer);
if (channelId == 0)
{
parseControlBlocks(buffer);
}
else
{
parseDataFrame(buffer);
}
}
}
catch (MuxException e)
{
events.onMuxException(e);
}
catch (Throwable t)
{
events.onMuxException(new MuxException(t));
}
}
private void parseControlBlocks(ByteBuffer buffer)
{
// process the remaining buffer here.
while (buffer.remaining() > 0)
{
byte b = buffer.get();
byte opc = (byte)((byte)(b >> 5) & 0xFF);
b = (byte)(b & 0x1F);
try {
switch (opc)
{
case MuxOp.ADD_CHANNEL_REQUEST:
{
MuxAddChannelRequest op = new MuxAddChannelRequest();
op.setRsv((byte)((b & 0x1C) >> 2));
op.setEncoding((byte)(b & 0x03));
op.setChannelId(readChannelId(buffer));
long handshakeSize = read139EncodedSize(buffer);
op.setHandshake(readBlock(buffer,handshakeSize));
events.onMuxAddChannelRequest(op);
break;
}
case MuxOp.ADD_CHANNEL_RESPONSE:
{
MuxAddChannelResponse op = new MuxAddChannelResponse();
op.setFailed((b & 0x10) != 0);
op.setRsv((byte)((byte)(b & 0x0C) >> 2));
op.setEncoding((byte)(b & 0x03));
op.setChannelId(readChannelId(buffer));
long handshakeSize = read139EncodedSize(buffer);
op.setHandshake(readBlock(buffer,handshakeSize));
events.onMuxAddChannelResponse(op);
break;
}
case MuxOp.DROP_CHANNEL:
{
int rsv = (b & 0x1F);
long channelId = readChannelId(buffer);
long reasonSize = read139EncodedSize(buffer);
ByteBuffer reasonBuf = readBlock(buffer,reasonSize);
MuxDropChannel op = MuxDropChannel.parse(channelId,reasonBuf);
op.setRsv(rsv);
events.onMuxDropChannel(op);
break;
}
case MuxOp.FLOW_CONTROL:
{
MuxFlowControl op = new MuxFlowControl();
op.setRsv((byte)(b & 0x1F));
op.setChannelId(readChannelId(buffer));
op.setSendQuotaSize(read139EncodedSize(buffer));
events.onMuxFlowControl(op);
break;
}
case MuxOp.NEW_CHANNEL_SLOT:
{
MuxNewChannelSlot op = new MuxNewChannelSlot();
op.setRsv((byte)((b & 0x1E) >> 1));
op.setFallback((b & 0x01) != 0);
op.setNumberOfSlots(read139EncodedSize(buffer));
op.setInitialSendQuota(read139EncodedSize(buffer));
events.onMuxNewChannelSlot(op);
break;
}
default:
{
String err = String.format("Unknown Mux Control Code OPC [0x%X]",opc);
throw new MuxException(err);
}
}
}
catch (Throwable t)
{
LOG.warn(t);
throw new MuxException(t);
}
}
}
private void parseDataFrame(ByteBuffer buffer)
{
byte b = buffer.get();
boolean fin = ((b & 0x80) != 0);
boolean rsv1 = ((b & 0x40) != 0);
boolean rsv2 = ((b & 0x20) != 0);
boolean rsv3 = ((b & 0x10) != 0);
byte opcode = (byte)(b & 0x0F);
if (opcode == OpCode.CONTINUATION)
{
muxframe.setIsContinuation();
}
else
{
muxframe.reset();
muxframe.setOp(opcode);
}
muxframe.setChannelId(channelId);
muxframe.setFin(fin);
muxframe.setRsv1(rsv1);
muxframe.setRsv2(rsv2);
muxframe.setRsv3(rsv3);
parseDataFramePayload(buffer);
}
private void parseDataFramePayload(ByteBuffer buffer)
{
int capacity = buffer.remaining();
ByteBuffer payload = ByteBuffer.allocate(capacity);
payload.put(buffer);
BufferUtil.flipToFlush(payload,0);
muxframe.setPayload(payload);
try
{
LOG.debug("notifyFrame() - {}",muxframe);
events.onMuxedFrame(muxframe);
}
catch (Throwable t)
{
LOG.warn(t);
}
}
/**
* Per section <a href="https://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing#section-9.1">9.1. Number Encoding in Multiplex Control
* Blocks</a>, read the 1/3/9 byte length using <a href="https://tools.ietf.org/html/rfc6455#section-5.2">Section 5.2 of RFC 6455</a>.
*
* @param buffer
* the buffer to read from
* @return the decoded size
* @throws MuxException
* when the encoding does not make sense per the spec, or it is a value above {@link Long#MAX_VALUE}
*/
public long read139EncodedSize(ByteBuffer buffer)
{
long ret = -1;
long minValue = 0x00; // used to validate minimum # of bytes (per spec)
int cursor = 0;
byte b = buffer.get();
ret = (b & 0x7F);
if (ret == 0x7F)
{
// 9 byte length
ret = 0;
minValue = 0xFF_FF;
cursor = 8;
}
else if (ret == 0x7E)
{
// 3 byte length
ret = 0;
minValue = 0x7F;
cursor = 2;
}
else
{
// 1 byte length
// no validation of minimum bytes needed here
return ret;
}
// parse multi-byte length
while (cursor > 0)
{
ret = ret << 8;
b = buffer.get();
ret |= (b & 0xFF);
--cursor;
}
// validate minimum value per spec.
if (ret <= minValue)
{
String err = String.format("Invalid 1/3/9 length 0x%X (minimum value for chosen encoding is 0x%X)",ret,minValue);
throw new MuxException(err);
}
return ret;
}
private ByteBuffer readBlock(ByteBuffer buffer, long size)
{
if (size == 0)
{
return null;
}
if (size > buffer.remaining())
{
String err = String.format("Truncated data, expected %,d byte(s), but only %,d byte(s) remain",size,buffer.remaining());
throw new MuxException(err);
}
if (size > Integer.MAX_VALUE)
{
String err = String.format("[Int-Sane!] Buffer size %,d is too large to be supported (max allowed is %,d)",size,Integer.MAX_VALUE);
throw new MuxException(err);
}
ByteBuffer ret = ByteBuffer.allocate((int)size);
BufferUtil.put(buffer,ret);
BufferUtil.flipToFlush(ret,0);
return ret;
}
/**
* Read Channel ID using <a href="https://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing#section-7">Section 7. Framing</a> techniques
*
* @param buffer
* the buffer to parse from.
* @return the channel Id
* @throws MuxException
* when the encoding does not make sense per the spec.
*/
public long readChannelId(ByteBuffer buffer)
{
long id = -1;
long minValue = 0x00; // used to validate minimum # of bytes (per spec)
byte b = buffer.get();
int cursor = -1;
if ((b & 0x80) == 0)
{
// 7 bit channel id
// no validation of minimum bytes needed here
return (b & 0x7F);
}
else if ((b & 0x40) == 0)
{
// 14 bit channel id
id = (b & 0x3F);
minValue = 0x7F;
cursor = 1;
}
else if ((b & 0x20) == 0)
{
// 21 bit channel id
id = (b & 0x1F);
minValue = 0x3F_FF;
cursor = 2;
}
else
{
// 29 bit channel id
id = (b & 0x1F);
minValue = 0x1F_FF_FF;
cursor = 3;
}
while (cursor > 0)
{
id = id << 8;
b = buffer.get();
id |= (b & 0xFF);
--cursor;
}
// validate minimum value per spec.
if (id <= minValue)
{
String err = String.format("Invalid Channel ID 0x%X (minimum value for chosen encoding is 0x%X)",id,minValue);
throw new MuxException(err);
}
return id;
}
public void setEvents(MuxParser.Listener events)
{
this.events = events;
}
}

View File

@ -1,44 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.mux.op.MuxDropChannel;
public class MuxPhysicalConnectionException extends MuxException
{
private static final long serialVersionUID = 1L;
private MuxDropChannel drop;
public MuxPhysicalConnectionException(MuxDropChannel.Reason code, String phrase)
{
super(phrase);
drop = new MuxDropChannel(0,code,phrase);
}
public MuxPhysicalConnectionException(MuxDropChannel.Reason code, String phrase, Throwable t)
{
super(phrase,t);
drop = new MuxDropChannel(0,code,phrase);
}
public MuxDropChannel getMuxDropChannel()
{
return drop;
}
}

View File

@ -1,63 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
public class MuxRequest extends UpgradeRequest
{
public static final String HEADER_VALUE_DELIM="\"\\\n\r\t\f\b%+ ;=";
public static UpgradeRequest merge(UpgradeRequest baseReq, UpgradeRequest deltaReq)
{
MuxRequest req = new MuxRequest(baseReq);
// TODO: finish
return req;
}
private static String overlay(String val, String defVal)
{
if (val == null)
{
return defVal;
}
return val;
}
public static UpgradeRequest parse(ByteBuffer handshake)
{
MuxRequest req = new MuxRequest();
// TODO Auto-generated method stub
return req;
}
public MuxRequest()
{
super();
}
public MuxRequest(UpgradeRequest copy)
{
super();
}
}

View File

@ -1,26 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
public class MuxResponse extends UpgradeResponse
{
}

View File

@ -1,78 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.frames.DataFrame;
public class MuxedFrame extends DataFrame
{
private long channelId = -1;
public MuxedFrame()
{
super(OpCode.BINARY);
}
public MuxedFrame(MuxedFrame frame)
{
super(frame);
this.channelId = frame.channelId;
}
public long getChannelId()
{
return channelId;
}
@Override
public void reset()
{
super.reset();
this.channelId = -1;
}
public void setChannelId(long channelId)
{
this.channelId = channelId;
}
@Override
public String toString()
{
StringBuilder b = new StringBuilder();
b.append(OpCode.name(getOpCode()));
b.append('[');
b.append("channel=").append(channelId);
b.append(",len=").append(getPayloadLength());
b.append(",fin=").append(isFin());
b.append(",rsv=");
b.append(isRsv1()?'1':'.');
b.append(isRsv2()?'1':'.');
b.append(isRsv3()?'1':'.');
b.append(']');
return b.toString();
}
public void setOp(byte opcode)
{
// TODO Auto-generated method stub
}
}

View File

@ -1,439 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.frames.ControlFrame;
import org.eclipse.jetty.websocket.mux.add.MuxAddClient;
import org.eclipse.jetty.websocket.mux.add.MuxAddServer;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.eclipse.jetty.websocket.mux.op.MuxDropChannel;
import org.eclipse.jetty.websocket.mux.op.MuxFlowControl;
import org.eclipse.jetty.websocket.mux.op.MuxNewChannelSlot;
/**
* Muxer responsible for managing sub-channels.
* <p>
* Maintains a 1 (incoming and outgoing mux encapsulated frames) to many (per-channel incoming/outgoing standard websocket frames) relationship, along with
* routing of {@link MuxControlBlock} events.
* <p>
* Control Channel events (channel ID == 0) are handled by the Muxer.
*/
public class Muxer implements IncomingFrames, MuxParser.Listener
{
private static final int CONTROL_CHANNEL_ID = 0;
private static final Logger LOG = Log.getLogger(Muxer.class);
/**
* Map of sub-channels, key is the channel Id.
*/
private Map<Long, MuxChannel> channels = new HashMap<Long, MuxChannel>();
private final WebSocketPolicy policy;
private final LogicalConnection physicalConnection;
private InetSocketAddress remoteAddress;
/** Parsing frames destined for sub-channels */
private MuxParser parser;
/** Generating frames destined for physical connection */
private MuxGenerator generator;
private MuxAddServer addServer;
private MuxAddClient addClient;
/** The original request headers, used for delta encoded AddChannelRequest blocks */
private UpgradeRequest physicalRequestHeaders;
/** The original response headers, used for delta encoded AddChannelResponse blocks */
private UpgradeResponse physicalResponseHeaders;
public Muxer(final LogicalConnection connection)
{
this.physicalConnection = connection;
this.policy = connection.getPolicy().clonePolicy();
this.parser = new MuxParser();
this.parser.setEvents(this);
this.generator = new MuxGenerator();
}
public MuxAddClient getAddClient()
{
return addClient;
}
public MuxAddServer getAddServer()
{
return addServer;
}
public MuxChannel getChannel(long channelId, boolean create)
{
if (channelId == CONTROL_CHANNEL_ID)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"Invalid Channel ID");
}
MuxChannel channel = channels.get(channelId);
if (channel == null)
{
if (create)
{
channel = new MuxChannel(channelId,this);
channels.put(channelId,channel);
}
else
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"Unknown Channel ID");
}
}
return channel;
}
public WebSocketPolicy getPolicy()
{
return policy;
}
/**
* Get the remote address of the physical connection.
*
* @return the remote address of the physical connection
*/
public InetSocketAddress getRemoteAddress()
{
return this.remoteAddress;
}
/**
* Incoming parser errors
*/
@Override
public void incomingError(Throwable e)
{
MuxDropChannel.Reason reason = MuxDropChannel.Reason.PHYSICAL_CONNECTION_FAILED;
String phrase = String.format("%s: %s", e.getClass().getName(), e.getMessage());
mustFailPhysicalConnection(new MuxPhysicalConnectionException(reason,phrase));
}
/**
* Incoming mux encapsulated frames.
*/
@Override
public void incomingFrame(Frame frame)
{
parser.parse(frame);
}
/**
* Is the muxer and the physical connection still open?
*
* @return true if open
*/
public boolean isOpen()
{
return physicalConnection.isOpen();
}
public String mergeHeaders(List<String> physicalHeaders, String deltaHeaders)
{
// TODO Auto-generated method stub
return null;
}
/**
* Per spec, the physical connection must be failed.
* <p>
* <a href="https://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-08#section-18">Section 18. Fail the Physical Connection.</a>
*
* <blockquote> To _Fail the Physical Connection_, an endpoint MUST send a DropChannel multiplex control block with objective channel ID of 0 and drop
* reason code in the range of 2000-2999, and then _Fail the WebSocket Connection_ on the physical connection with status code of 1011. </blockquote>
*/
private void mustFailPhysicalConnection(MuxPhysicalConnectionException muxe)
{
// TODO: stop muxer from receiving incoming sub-channel traffic.
MuxDropChannel drop = muxe.getMuxDropChannel();
LOG.warn(muxe);
try
{
generator.generate(null,drop);
}
catch (IOException ioe)
{
LOG.warn("Unable to send mux DropChannel",ioe);
}
String reason = "Mux[MUST FAIL]" + drop.getPhrase();
reason = StringUtil.truncate(reason,ControlFrame.MAX_CONTROL_PAYLOAD);
this.physicalConnection.close(StatusCode.SERVER_ERROR,reason);
// TODO: trigger abnormal close for all sub-channels.
}
/**
* Incoming mux control block, destined for the control channel (id 0)
*/
@Override
public void onMuxAddChannelRequest(MuxAddChannelRequest request)
{
if (policy.getBehavior() == WebSocketBehavior.CLIENT)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"AddChannelRequest not allowed per spec");
}
if (request.getRsv() != 0)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_REQUEST_ENCODING,"RSV Not allowed to be set");
}
// Pre-allocate channel.
long channelId = request.getChannelId();
MuxChannel channel = getChannel(channelId, true);
// submit to upgrade handshake process
try
{
switch (request.getEncoding())
{
case MuxAddChannelRequest.IDENTITY_ENCODING:
{
UpgradeRequest idenReq = MuxRequest.parse(request.getHandshake());
addServer.handshake(this,channel,idenReq);
break;
}
case MuxAddChannelRequest.DELTA_ENCODING:
{
UpgradeRequest baseReq = addServer.getPhysicalHandshakeRequest();
UpgradeRequest deltaReq = MuxRequest.parse(request.getHandshake());
UpgradeRequest mergedReq = MuxRequest.merge(baseReq,deltaReq);
addServer.handshake(this,channel,mergedReq);
break;
}
default:
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.BAD_REQUEST,"Unrecognized request encoding");
}
}
}
catch (MuxPhysicalConnectionException e)
{
throw e;
}
catch (Throwable t)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.BAD_REQUEST,"Unable to parse request",t);
}
}
/**
* Incoming mux control block, destined for the control channel (id 0)
*/
@Override
public void onMuxAddChannelResponse(MuxAddChannelResponse response)
{
if (policy.getBehavior() == WebSocketBehavior.SERVER)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"AddChannelResponse not allowed per spec");
}
if (response.getRsv() != 0)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_RESPONSE_ENCODING,"RSV Not allowed to be set");
}
// Process channel
long channelId = response.getChannelId();
MuxChannel channel = getChannel(channelId,false);
// Process Response headers
try
{
// Parse Response
// TODO: Sec-WebSocket-Accept header
// TODO: Sec-WebSocket-Extensions header
// TODO: Setup extensions
// TODO: Setup sessions
// Trigger channel open
channel.onOpen();
}
catch (Throwable t)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.BAD_RESPONSE,"Unable to parse response",t);
}
}
/**
* Incoming mux control block, destined for the control channel (id 0)
*/
@Override
public void onMuxDropChannel(MuxDropChannel drop)
{
// Process channel
long channelId = drop.getChannelId();
MuxChannel channel = getChannel(channelId,false);
String reason = "Mux " + drop.toString();
reason = StringUtil.truncate(reason,(ControlFrame.MAX_CONTROL_PAYLOAD - 2));
channel.close(StatusCode.PROTOCOL,reason);
// TODO: set channel to inactive?
}
/**
* Incoming mux-unwrapped frames, destined for a sub-channel
*/
@Override
public void onMuxedFrame(MuxedFrame frame)
{
MuxChannel subchannel = channels.get(frame.getChannelId());
subchannel.incomingFrame(frame);
}
@Override
public void onMuxException(MuxException e)
{
if (e instanceof MuxPhysicalConnectionException)
{
mustFailPhysicalConnection((MuxPhysicalConnectionException)e);
}
LOG.warn(e);
// TODO: handle other (non physical) mux exceptions how?
}
/**
* Incoming mux control block, destined for the control channel (id 0)
*/
@Override
public void onMuxFlowControl(MuxFlowControl flow)
{
if (flow.getSendQuotaSize() > 0x7F_FF_FF_FF_FF_FF_FF_FFL)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.SEND_QUOTA_OVERFLOW,"Send Quota Overflow");
}
// Process channel
long channelId = flow.getChannelId();
MuxChannel channel = getChannel(channelId,false);
// TODO: set channel quota
}
/**
* Incoming mux control block, destined for the control channel (id 0)
*/
@Override
public void onMuxNewChannelSlot(MuxNewChannelSlot slot)
{
if (policy.getBehavior() == WebSocketBehavior.SERVER)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"NewChannelSlot not allowed per spec");
}
if (slot.isFallback())
{
if (slot.getNumberOfSlots() == 0)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"Cannot have 0 number of slots during fallback");
}
if (slot.getInitialSendQuota() == 0)
{
throw new MuxPhysicalConnectionException(MuxDropChannel.Reason.UNKNOWN_MUX_CONTROL_BLOCK,"Cannot have 0 initial send quota during fallback");
}
}
// TODO: handle channel slot
}
/**
* Outgoing frame, without mux encapsulated payload.
*/
public void output(long channelId, Frame frame, WriteCallback callback)
{
if (LOG.isDebugEnabled())
{
LOG.debug("output({}, {})",channelId,frame,callback);
}
generator.generate(channelId,frame,callback);
}
/**
* Write an OP out the physical connection.
*
* @param op
* the mux operation to write
* @throws IOException
*/
public void output(MuxControlBlock op) throws IOException
{
generator.generate(null,op);
}
public void setAddClient(MuxAddClient addClient)
{
this.addClient = addClient;
}
public void setAddServer(MuxAddServer addServer)
{
this.addServer = addServer;
}
public void setOutgoingFramesHandler(OutgoingFrames outgoing)
{
this.generator.setOutgoing(outgoing);
}
/**
* Set the remote address of the physical connection.
* <p>
* This address made available to sub-channels.
*
* @param remoteAddress
* the remote address
*/
public void setRemoteAddress(InetSocketAddress remoteAddress)
{
this.remoteAddress = remoteAddress;
}
@Override
public String toString()
{
return String.format("Muxer[subChannels.size=%d]",channels.size());
}
}

View File

@ -1,30 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
/**
* Interface for Mux Client to handle receiving a AddChannelResponse
*/
public interface MuxAddClient
{
WebSocketSession createSession(MuxAddChannelResponse response);
}

View File

@ -1,52 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.mux.MuxChannel;
import org.eclipse.jetty.websocket.mux.MuxException;
import org.eclipse.jetty.websocket.mux.Muxer;
/**
* Server interface, for dealing with incoming AddChannelRequest / AddChannelResponse flows.
*/
public interface MuxAddServer
{
public UpgradeRequest getPhysicalHandshakeRequest();
public UpgradeResponse getPhysicalHandshakeResponse();
/**
* Perform the handshake.
*
* @param channel
* the channel to attach the {@link WebSocketSession} to.
* @param requestHandshake
* the request handshake (request headers)
* @throws MuxException
* if unable to handshake
* @throws IOException
* if unable to parse request headers
*/
void handshake(Muxer muxer, MuxChannel channel, UpgradeRequest request) throws MuxException, IOException;
}

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
/**
* Jetty WebSocket Common : MUX Extension Add Channel Handling [<em>Unstable Early Draft</em>]
*/
package org.eclipse.jetty.websocket.mux.add;

View File

@ -1,33 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.client;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.mux.add.MuxAddClient;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
public class MuxClientAddHandler implements MuxAddClient
{
@Override
public WebSocketSession createSession(MuxAddChannelResponse response)
{
// TODO Auto-generated method stub
return null;
}
}

View File

@ -1,31 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.client;
import org.eclipse.jetty.websocket.mux.AbstractMuxExtension;
import org.eclipse.jetty.websocket.mux.Muxer;
public class MuxClientExtension extends AbstractMuxExtension
{
@Override
public void configureMuxer(Muxer muxer)
{
muxer.setAddClient(new MuxClientAddHandler());
}
}

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
/**
* Jetty WebSocket Client : MUX Extension [<em>Unstable Early Draft</em>]
*/
package org.eclipse.jetty.websocket.mux.client;

View File

@ -1,114 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.op;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.mux.MuxControlBlock;
import org.eclipse.jetty.websocket.mux.MuxOp;
public class MuxAddChannelRequest implements MuxControlBlock
{
public static final byte IDENTITY_ENCODING = (byte)0x00;
public static final byte DELTA_ENCODING = (byte)0x01;
private long channelId = -1;
private byte encoding;
private ByteBuffer handshake;
private byte rsv;
public long getChannelId()
{
return channelId;
}
public byte getEncoding()
{
return encoding;
}
public ByteBuffer getHandshake()
{
return handshake;
}
public long getHandshakeSize()
{
if (handshake == null)
{
return 0;
}
return handshake.remaining();
}
@Override
public int getOpCode()
{
return MuxOp.ADD_CHANNEL_REQUEST;
}
public byte getRsv()
{
return rsv;
}
public boolean isDeltaEncoded()
{
return (encoding == DELTA_ENCODING);
}
public boolean isIdentityEncoded()
{
return (encoding == IDENTITY_ENCODING);
}
public void setChannelId(long channelId)
{
this.channelId = channelId;
}
public void setEncoding(byte enc)
{
this.encoding = enc;
}
public void setHandshake(ByteBuffer handshake)
{
if (handshake == null)
{
this.handshake = null;
}
else
{
this.handshake = handshake.slice();
}
}
public void setHandshake(String rawstring)
{
setHandshake(BufferUtil.toBuffer(rawstring, StandardCharsets.UTF_8));
}
public void setRsv(byte rsv)
{
this.rsv = rsv;
}
}

View File

@ -1,125 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.op;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.mux.MuxControlBlock;
import org.eclipse.jetty.websocket.mux.MuxOp;
public class MuxAddChannelResponse implements MuxControlBlock
{
public static final byte IDENTITY_ENCODING = (byte)0x00;
public static final byte DELTA_ENCODING = (byte)0x01;
private long channelId;
private byte encoding;
private byte rsv;
private boolean failed = false;
private ByteBuffer handshake;
public long getChannelId()
{
return channelId;
}
public byte getEncoding()
{
return encoding;
}
public ByteBuffer getHandshake()
{
return handshake;
}
public long getHandshakeSize()
{
if (handshake == null)
{
return 0;
}
return handshake.remaining();
}
@Override
public int getOpCode()
{
return MuxOp.ADD_CHANNEL_RESPONSE;
}
public byte getRsv()
{
return rsv;
}
public boolean isDeltaEncoded()
{
return (encoding == DELTA_ENCODING);
}
public boolean isFailed()
{
return failed;
}
public boolean isIdentityEncoded()
{
return (encoding == IDENTITY_ENCODING);
}
public void setChannelId(long channelId)
{
this.channelId = channelId;
}
public void setEncoding(byte enc)
{
this.encoding = enc;
}
public void setFailed(boolean failed)
{
this.failed = failed;
}
public void setHandshake(ByteBuffer handshake)
{
if (handshake == null)
{
this.handshake = null;
}
else
{
this.handshake = handshake.slice();
}
}
public void setHandshake(String responseHandshake)
{
setHandshake(BufferUtil.toBuffer(responseHandshake, StandardCharsets.UTF_8));
}
public void setRsv(byte rsv)
{
this.rsv = rsv;
}
}

View File

@ -1,183 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.op;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.websocket.mux.MuxControlBlock;
import org.eclipse.jetty.websocket.mux.MuxOp;
public class MuxDropChannel implements MuxControlBlock
{
/**
* Outlined in <a href="https://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-05#section-9.4.1">Section 9.4.1. Drop Reason Codes</a>
*/
public static enum Reason
{
// Normal Close : (1000-1999)
NORMAL_CLOSURE(1000),
// Failures in Physical Connection : (2000-2999)
PHYSICAL_CONNECTION_FAILED(2000),
INVALID_ENCAPSULATING_MESSAGE(2001),
CHANNEL_ID_TRUNCATED(2002),
ENCAPSULATED_FRAME_TRUNCATED(2003),
UNKNOWN_MUX_CONTROL_OPC(2004),
UNKNOWN_MUX_CONTROL_BLOCK(2005),
CHANNEL_ALREADY_EXISTS(2006),
NEW_CHANNEL_SLOT_VIOLATION(2007),
NEW_CHANNEL_SLOT_OVERFLOW(2008),
BAD_REQUEST(2009),
UNKNOWN_REQUEST_ENCODING(2010),
BAD_RESPONSE(2011),
UNKNOWN_RESPONSE_ENCODING(2012),
// Failures in Logical Connections : (3000-3999)
LOGICAL_CHANNEL_FAILED(3000),
SEND_QUOTA_VIOLATION(3005),
SEND_QUOTA_OVERFLOW(3006),
IDLE_TIMEOUT(3007),
DROP_CHANNEL_ACK(3008),
// Other Peer Actions : (4000-4999)
USE_ANOTHER_PHYSICAL_CONNECTION(4001),
BUSY(4002);
private static final Map<Integer, Reason> codeMap;
static
{
codeMap = new HashMap<>();
for (Reason r : values())
{
codeMap.put(r.getValue(),r);
}
}
public static Reason valueOf(int code)
{
return codeMap.get(code);
}
private final int code;
private Reason(int code)
{
this.code = code;
}
public int getValue()
{
return code;
}
}
public static MuxDropChannel parse(long channelId, ByteBuffer payload)
{
// TODO Auto-generated method stub
return null;
}
private final long channelId;
private final Reason code;
private String phrase;
private int rsv;
/**
* Normal Drop. no reason Phrase.
*
* @param channelId
* the logical channel Id to perform drop against.
*/
public MuxDropChannel(long channelId)
{
this(channelId,Reason.NORMAL_CLOSURE,null);
}
/**
* Drop with reason code and optional phrase
*
* @param channelId
* the logical channel Id to perform drop against.
* @param code
* reason code
* @param phrase
* optional human readable phrase
*/
public MuxDropChannel(long channelId, int code, String phrase)
{
this(channelId, Reason.valueOf(code), phrase);
}
/**
* Drop with reason code and optional phrase
*
* @param channelId
* the logical channel Id to perform drop against.
* @param code
* reason code
* @param phrase
* optional human readable phrase
*/
public MuxDropChannel(long channelId, Reason code, String phrase)
{
this.channelId = channelId;
this.code = code;
this.phrase = phrase;
}
public ByteBuffer asReasonBuffer()
{
// TODO: convert to reason buffer
return null;
}
public long getChannelId()
{
return channelId;
}
public Reason getCode()
{
return code;
}
@Override
public int getOpCode()
{
return MuxOp.DROP_CHANNEL;
}
public String getPhrase()
{
return phrase;
}
public int getRsv()
{
return rsv;
}
public void setRsv(int rsv)
{
this.rsv = rsv;
}
}

View File

@ -1,65 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.op;
import org.eclipse.jetty.websocket.mux.MuxControlBlock;
import org.eclipse.jetty.websocket.mux.MuxOp;
public class MuxFlowControl implements MuxControlBlock
{
private long channelId;
private byte rsv;
private long sendQuotaSize;
public long getChannelId()
{
return channelId;
}
@Override
public int getOpCode()
{
return MuxOp.FLOW_CONTROL;
}
public byte getRsv()
{
return rsv;
}
public long getSendQuotaSize()
{
return sendQuotaSize;
}
public void setChannelId(long channelId)
{
this.channelId = channelId;
}
public void setRsv(byte rsv)
{
this.rsv = rsv;
}
public void setSendQuotaSize(long sendQuotaSize)
{
this.sendQuotaSize = sendQuotaSize;
}
}

View File

@ -1,76 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.op;
import org.eclipse.jetty.websocket.mux.MuxControlBlock;
import org.eclipse.jetty.websocket.mux.MuxOp;
public class MuxNewChannelSlot implements MuxControlBlock
{
private boolean fallback;
private long initialSendQuota;
private long numberOfSlots;
private byte rsv;
public long getInitialSendQuota()
{
return initialSendQuota;
}
public long getNumberOfSlots()
{
return numberOfSlots;
}
@Override
public int getOpCode()
{
return MuxOp.NEW_CHANNEL_SLOT;
}
public byte getRsv()
{
return rsv;
}
public boolean isFallback()
{
return fallback;
}
public void setFallback(boolean fallback)
{
this.fallback = fallback;
}
public void setInitialSendQuota(long initialSendQuota)
{
this.initialSendQuota = initialSendQuota;
}
public void setNumberOfSlots(long numberOfSlots)
{
this.numberOfSlots = numberOfSlots;
}
public void setRsv(byte rsv)
{
this.rsv = rsv;
}
}

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
/**
* Jetty WebSocket Common : MUX Extension OpCode Handling [<em>Unstable Early Draft</em>]
*/
package org.eclipse.jetty.websocket.mux.op;

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
/**
* Jetty WebSocket Common : MUX Extension Core [<em>Unstable Early Draft</em>]
*/
package org.eclipse.jetty.websocket.mux;

View File

@ -1,41 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.server;
import java.nio.ByteBuffer;
import org.eclipse.jetty.server.ByteBufferQueuedHttpInput;
/**
* HttpInput for Empty Http body sections.
*/
public class EmptyHttpInput extends ByteBufferQueuedHttpInput
{
@Override
protected int get(ByteBuffer item, byte[] buffer, int offset, int length)
{
return 0;
}
@Override
protected int remaining(ByteBuffer item)
{
return 0;
}
}

View File

@ -1,40 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.server;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.HttpTransport;
/**
* Process incoming AddChannelRequest headers within the existing Jetty framework. Benefiting from Server container knowledge and various webapp configuration
* knowledge.
*/
public class HttpChannelOverMux extends HttpChannel<ByteBuffer>
{
public HttpChannelOverMux(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput<ByteBuffer> input)
{
super(connector,configuration,endPoint,transport,input);
}
}

View File

@ -1,74 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.server;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.mux.MuxChannel;
import org.eclipse.jetty.websocket.mux.Muxer;
/**
* Take {@link ResponseInfo} objects and convert to bytes for response.
*/
public class HttpTransportOverMux implements HttpTransport
{
private static final Logger LOG = Log.getLogger(HttpTransportOverMux.class);
private final BlockingCallback streamBlocker = new BlockingCallback();
public HttpTransportOverMux(Muxer muxer, MuxChannel channel)
{
// TODO Auto-generated constructor stub
}
@Override
public void completed()
{
LOG.debug("completed");
}
@Override
public void send(ResponseInfo info, ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
{
if (lastContent == false)
{
// throw error
}
if (info.getContentLength() > 0)
{
// throw error
}
// prepare the AddChannelResponse
// TODO: look at HttpSender in jetty-client for generator loop logic
}
@Override
public void send(ByteBuffer responseBodyContent, boolean lastContent, Callback callback)
{
send(null,responseBodyContent, lastContent, callback);
}
}

View File

@ -1,112 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.server;
import java.io.IOException;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.mux.MuxChannel;
import org.eclipse.jetty.websocket.mux.MuxException;
import org.eclipse.jetty.websocket.mux.Muxer;
import org.eclipse.jetty.websocket.mux.add.MuxAddServer;
/**
* Handler for incoming MuxAddChannel requests.
*/
public class MuxAddHandler implements MuxAddServer
{
/** Represents physical connector */
private Connector connector;
/** Used for local address */
private EndPoint endPoint;
/** The original request handshake */
private UpgradeRequest baseHandshakeRequest;
/** The original request handshake */
private UpgradeResponse baseHandshakeResponse;
private int maximumHeaderSize = 32 * 1024;
@Override
public UpgradeRequest getPhysicalHandshakeRequest()
{
// TODO Auto-generated method stub
return null;
}
@Override
public UpgradeResponse getPhysicalHandshakeResponse()
{
// TODO Auto-generated method stub
return null;
}
/**
* An incoming MuxAddChannel request.
*
* @param muxer the muxer handling this
* @param channel the
* channel this request should be bound to
* @param request
* the incoming request headers (complete and merged if delta encoded)
*/
@Override
public void handshake(Muxer muxer, MuxChannel channel, UpgradeRequest request) throws MuxException, IOException
{
// Need to call into HttpChannel to get the websocket properly setup.
HttpTransportOverMux transport = new HttpTransportOverMux(muxer,channel);
EmptyHttpInput input = new EmptyHttpInput();
HttpConfiguration configuration = new HttpConfiguration();
HttpChannelOverMux httpChannel = new HttpChannelOverMux(//
connector,configuration,endPoint,transport,input);
HttpMethod method = HttpMethod.fromString(request.getMethod());
HttpVersion version = HttpVersion.fromString(request.getHttpVersion());
httpChannel.startRequest(method,request.getMethod(),BufferUtil.toBuffer(request.getRequestURI().toASCIIString()),version);
for (String headerName : request.getHeaders().keySet())
{
HttpHeader header = HttpHeader.CACHE.getBest(headerName.getBytes(),0,headerName.length());
for (String value : request.getHeaders().get(headerName))
{
httpChannel.parsedHeader(new HttpField(header,value));
}
}
httpChannel.headerComplete();
httpChannel.messageComplete();
httpChannel.run(); // calls into server for appropriate resource
// TODO: what's in request handshake is not enough to process the request.
// like a partial http request. (consider this a AddChannelRequest failure)
throw new MuxException("Not a valid request");
}
}

View File

@ -1,31 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.server;
import org.eclipse.jetty.websocket.mux.AbstractMuxExtension;
import org.eclipse.jetty.websocket.mux.Muxer;
public class MuxServerExtension extends AbstractMuxExtension
{
@Override
public void configureMuxer(Muxer muxer)
{
muxer.setAddServer(new MuxAddHandler());
}
}

View File

@ -1,23 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
/**
* Jetty WebSocket Server : MUX Extension [<em>Unstable Early Draft</em>]
*/
package org.eclipse.jetty.websocket.mux.server;

View File

@ -1 +0,0 @@
org.eclipse.jetty.websocket.mux.client.MuxClientExtension

View File

@ -1 +0,0 @@
org.eclipse.jetty.websocket.mux.client.MuxServerExtension

View File

@ -1,47 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package examples.echo;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
/**
* Example EchoSocket using Adapter.
*/
public class AdapterEchoSocket extends WebSocketAdapter
{
@Override
public void onWebSocketText(String message)
{
if (isConnected())
{
try
{
System.out.printf("Echoing back message [%s]%n",message);
// echo the message back
getRemote().sendString(message);
}
catch (IOException e)
{
e.printStackTrace(System.err);
}
}
}
}

View File

@ -1,45 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
/**
* Helpful utility class to parse arbitrary mux events from a physical connection's OutgoingFrames.
*
* @see MuxEncoder
*/
public class MuxDecoder extends MuxEventCapture implements OutgoingFrames
{
private MuxParser parser;
public MuxDecoder()
{
parser = new MuxParser();
parser.setEvents(this);
}
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
parser.parse(frame);
}
}

View File

@ -1,64 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import java.io.IOException;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.io.FramePipes;
/**
* Helpful utility class to send arbitrary mux events into a physical connection's IncomingFrames.
*
* @see MuxDecoder
*/
public class MuxEncoder
{
public static MuxEncoder toIncoming(IncomingFrames incoming)
{
return new MuxEncoder(FramePipes.to(incoming));
}
public static MuxEncoder toOutgoing(OutgoingFrames outgoing)
{
return new MuxEncoder(outgoing);
}
private MuxGenerator generator;
private MuxEncoder(OutgoingFrames outgoing)
{
this.generator = new MuxGenerator();
this.generator.setOutgoing(outgoing);
}
public void frame(long channelId, WebSocketFrame frame) throws IOException
{
this.generator.generate(channelId,frame,null);
}
public void op(MuxControlBlock op) throws IOException
{
WriteCallback callback = null;
this.generator.generate(callback,op);
}
}

View File

@ -1,137 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.util.LinkedList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.eclipse.jetty.websocket.mux.op.MuxDropChannel;
import org.eclipse.jetty.websocket.mux.op.MuxFlowControl;
import org.eclipse.jetty.websocket.mux.op.MuxNewChannelSlot;
import org.junit.Assert;
public class MuxEventCapture implements MuxParser.Listener
{
private static final Logger LOG = Log.getLogger(MuxEventCapture.class);
private LinkedList<MuxedFrame> frames = new LinkedList<>();
private LinkedList<MuxControlBlock> ops = new LinkedList<>();
private LinkedList<MuxException> errors = new LinkedList<>();
public void assertFrameCount(int expected)
{
Assert.assertThat("Frame Count",frames.size(), is(expected));
}
public void assertHasFrame(byte opcode, long channelId, int expectedCount)
{
int actualCount = 0;
for (MuxedFrame frame : frames)
{
if (frame.getChannelId() == channelId)
{
if (frame.getOpCode() == opcode)
{
actualCount++;
}
}
}
Assert.assertThat("Expected Count of " + OpCode.name(opcode) + " frames on Channel ID " + channelId,actualCount,is(expectedCount));
}
public void assertHasOp(byte opCode, int expectedCount)
{
int actualCount = 0;
for (MuxControlBlock block : ops)
{
if (block.getOpCode() == opCode)
{
actualCount++;
}
}
Assert.assertThat("Op[" + opCode + "] count",actualCount,is(expectedCount));
}
public LinkedList<MuxedFrame> getFrames()
{
return frames;
}
public LinkedList<MuxControlBlock> getOps()
{
return ops;
}
@Override
public void onMuxAddChannelRequest(MuxAddChannelRequest request)
{
ops.add(request);
}
@Override
public void onMuxAddChannelResponse(MuxAddChannelResponse response)
{
ops.add(response);
}
@Override
public void onMuxDropChannel(MuxDropChannel drop)
{
ops.add(drop);
}
@Override
public void onMuxedFrame(MuxedFrame frame)
{
frames.add(new MuxedFrame(frame));
}
@Override
public void onMuxException(MuxException e)
{
LOG.debug(e);
errors.add(e);
}
@Override
public void onMuxFlowControl(MuxFlowControl flow)
{
ops.add(flow);
}
@Override
public void onMuxNewChannelSlot(MuxNewChannelSlot slot)
{
ops.add(slot);
}
public void reset()
{
frames.clear();
ops.clear();
}
}

View File

@ -1,97 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class MuxGeneratorWrite139SizeTest
{
private static MuxGenerator generator = new MuxGenerator();
@Parameters
public static Collection<Object[]> data()
{
// Various good 1/3/9 encodings
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
data.add(new Object[]{ 0L, "00"});
data.add(new Object[]{ 1L, "01"});
data.add(new Object[]{ 2L, "02"});
data.add(new Object[]{ 55L, "37"});
data.add(new Object[]{125L, "7D"});
// - 3 byte tests
data.add(new Object[]{0x00_80L, "7E0080"});
data.add(new Object[]{0x00_ABL, "7E00AB"});
data.add(new Object[]{0x00_FFL, "7E00FF"});
data.add(new Object[]{0x3F_FFL, "7E3FFF"});
// - 9 byte tests
data.add(new Object[]{0x00_00_01_FF_FFL, "7F000000000001FFFF"});
data.add(new Object[]{0x00_00_FF_FF_FFL, "7F0000000000FFFFFF"});
data.add(new Object[]{0x00_FF_FF_FF_FFL, "7F00000000FFFFFFFF"});
data.add(new Object[]{0xFF_FF_FF_FF_FFL, "7F000000FFFFFFFFFF"});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private long value;
private String expectedHex;
public MuxGeneratorWrite139SizeTest(long value, String expectedHex)
{
this.value = value;
this.expectedHex = expectedHex;
}
@Test
public void testWrite139Size()
{
System.err.printf("Running %s.%s - value: %,d%n",this.getClass().getName(),testname.getMethodName(),value);
ByteBuffer bbuf = ByteBuffer.allocate(10);
generator.write139Size(bbuf,value);
BufferUtil.flipToFlush(bbuf,0);
byte actual[] = BufferUtil.toArray(bbuf);
String actualHex = TypeUtil.toHexString(actual).toUpperCase(Locale.ENGLISH);
Assert.assertThat("1/3/9 encoded size of [" + value + "]",actualHex,is(expectedHex));
}
}

View File

@ -1,101 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests of valid ChannelID generation
*/
@RunWith(Parameterized.class)
public class MuxGeneratorWriteChannelIdTest
{
private static MuxGenerator generator = new MuxGenerator();
@Parameters
public static Collection<Object[]> data()
{
// Various good Channel IDs
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
data.add(new Object[]{ 0L, "00"});
data.add(new Object[]{ 1L, "01"});
data.add(new Object[]{ 2L, "02"});
data.add(new Object[]{ 55L, "37"});
data.add(new Object[]{127L, "7F"});
// - 2 byte tests
data.add(new Object[]{0x00_80L, "8080"});
data.add(new Object[]{0x00_FFL, "80FF"});
data.add(new Object[]{0x3F_FFL, "BFFF"});
// - 3 byte tests
data.add(new Object[]{0x00_FF_FFL, "C0FFFF"});
data.add(new Object[]{0x1F_FF_FFL, "DFFFFF"});
// - 3 byte tests
data.add(new Object[]{0x00_FF_FF_FFL, "E0FFFFFF"});
data.add(new Object[]{0x1F_FF_FF_FFL, "FFFFFFFF"});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private long channelId;
private String expectedHex;
public MuxGeneratorWriteChannelIdTest(long channelId, String expectedHex)
{
this.channelId = channelId;
this.expectedHex = expectedHex;
}
@Test
public void testReadChannelId()
{
System.err.printf("Running %s.%s - channelId: %,d%n",this.getClass().getName(),testname.getMethodName(),channelId);
ByteBuffer bbuf = ByteBuffer.allocate(10);
generator.writeChannelId(bbuf,channelId);
BufferUtil.flipToFlush(bbuf,0);
byte actual[] = BufferUtil.toArray(bbuf);
String actualHex = TypeUtil.toHexString(actual).toUpperCase(Locale.ENGLISH);
Assert.assertThat("Channel ID [" + channelId + "]",actualHex,is(expectedHex));
}
}

View File

@ -1,244 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.Parser;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.extensions.AbstractExtension;
import org.eclipse.jetty.websocket.mux.helper.IncomingFramesCapture;
import org.eclipse.jetty.websocket.mux.helper.UnitParser;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class MuxParserRFCTest
{
public static class DummyMuxExtension extends AbstractMuxExtension
{
@Override
public void configureMuxer(Muxer muxer)
{
/* nothing to do */
}
}
private LinkedList<WebSocketFrame> asFrames(byte[] buf)
{
IncomingFramesCapture capture = new IncomingFramesCapture();
WebSocketPolicy policy = WebSocketPolicy.newClientPolicy();
Parser parser = new UnitParser(policy);
parser.setIncomingFramesHandler(capture);
List<? extends AbstractExtension> muxList = Collections.singletonList(new DummyMuxExtension());
parser.configureFromExtensions(muxList);
ByteBuffer bbuf = ByteBuffer.wrap(buf);
parser.parse(bbuf);
return capture.getFrames();
}
private boolean isHexOnly(String part)
{
Pattern bytePat = Pattern.compile("(\\s*0x[0-9A-Fa-f]{2}+){1,}+");
Matcher mat = bytePat.matcher(part);
return mat.matches();
}
private MuxEventCapture parseMuxFrames(LinkedList<WebSocketFrame> frames)
{
MuxParser parser = new MuxParser();
MuxEventCapture capture = new MuxEventCapture();
parser.setEvents(capture);
for(Frame frame: frames) {
parser.parse(frame);
}
return capture;
}
@Test
public void testIsHexOnly()
{
Assert.assertTrue(isHexOnly("0x00"));
Assert.assertTrue(isHexOnly("0x00 0xaF"));
Assert.assertFalse(isHexOnly("Hello World"));
}
@Test
@Ignore
public void testRFCExample1() throws IOException
{
// Create RFC detailed frames
byte buf[] = toByteArray("0x82 0x0d 0x01 0x81","Hello world");
LinkedList<WebSocketFrame> frames = asFrames(buf);
Assert.assertThat("Frame count",frames.size(),is(1));
// Have mux parse frames
MuxEventCapture capture = parseMuxFrames(frames);
capture.assertFrameCount(1);
MuxedFrame mux;
mux = capture.getFrames().pop();
String prefix = "MuxFrame[0]";
Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(1L));
Assert.assertThat(prefix + ".fin",mux.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.TEXT));
String payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is("Hello world"));
}
@Test
@Ignore
public void testRFCExample2() throws IOException
{
// Create RFC detailed frames
byte buf[] = toByteArray("0x02 0x07 0x01 0x81","Hello","0x80 0x06"," world");
LinkedList<WebSocketFrame> frames = asFrames(buf);
Assert.assertThat("Frame count",frames.size(),is(2));
// Have mux parse frames
MuxEventCapture capture = parseMuxFrames(frames);
capture.assertFrameCount(2);
MuxedFrame mux;
// Text Frame
mux = capture.getFrames().get(0);
String prefix = "MuxFrame[0]";
Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(1L));
// (BUG IN DRAFT) Assert.assertThat(prefix + ".fin",mux.isFin(),is(false));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.TEXT));
String payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is("Hello"));
// Continuation Frame
mux = capture.getFrames().get(1);
prefix = "MuxFrame[1]";
// (BUG IN DRAFT) Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(1L));
// (BUG IN DRAFT) Assert.assertThat(prefix + ".fin",mux.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
// (BUG IN DRAFT) Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.BINARY));
payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is(" world"));
}
@Test
@Ignore
public void testRFCExample3() throws IOException
{
// Create RFC detailed frames
byte buf[] = toByteArray("0x82 0x07 0x01 0x01","Hello","0x82 0x05 0x02 0x81","bye","0x82 0x08 0x01 0x80"," world");
LinkedList<WebSocketFrame> frames = asFrames(buf);
Assert.assertThat("Frame count",frames.size(),is(3));
// Have mux parse frames
MuxEventCapture capture = parseMuxFrames(frames);
capture.assertFrameCount(3);
MuxedFrame mux;
// Text Frame (Message 1)
mux = capture.getFrames().pop();
String prefix = "MuxFrame[0]";
Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(1L));
Assert.assertThat(prefix + ".fin",mux.isFin(),is(false));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.TEXT));
String payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is("Hello"));
// Text Frame (Message 2)
mux = capture.getFrames().pop();
prefix = "MuxFrame[1]";
Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(2L));
Assert.assertThat(prefix + ".fin",mux.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.TEXT));
payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is("bye"));
// Continuation Frame (Message 1)
mux = capture.getFrames().pop();
prefix = "MuxFrame[2]";
Assert.assertThat(prefix + ".channelId",mux.getChannelId(),is(1L));
Assert.assertThat(prefix + ".fin",mux.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",mux.isRsv1(),is(false));
Assert.assertThat(prefix + ".rsv2",mux.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",mux.isRsv3(),is(false));
Assert.assertThat(prefix + ".masked",mux.isMasked(),is(false));
Assert.assertThat(prefix + ".opcode",mux.getOpCode(),is(OpCode.TEXT));
payload = mux.getPayloadAsUTF8();
Assert.assertThat(prefix + ".payload/text",payload,is(" world"));
}
private byte[] toByteArray(String... parts) throws IOException
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
for(String part: parts) {
if (isHexOnly(part))
{
String hexonly = part.replaceAll("\\s*0x","");
out.write(TypeUtil.fromHexString(hexonly));
}
else
{
out.write(part.getBytes(StandardCharsets.UTF_8));
}
}
return out.toByteArray();
}
}

View File

@ -1,104 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.containsString;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests for bad 1/3/9 size encoding.
*/
@RunWith(Parameterized.class)
public class MuxParserRead139Size_BadEncodingTest
{
private static MuxParser parser = new MuxParser();
@Parameters
public static Collection<Object[]> data()
{
// Various bad 1/3/9 encodings
// Violating "minimal number of bytes necessary" rule.
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
// all known 1 byte tests are valid
// - 3 byte tests
data.add(new Object[]{"7E0000"});
data.add(new Object[]{"7E0001"});
data.add(new Object[]{"7E0012"});
data.add(new Object[]{"7E0059"});
// extra bytes (not related to 1/3/9 size)
data.add(new Object[]{"7E0012345678"});
// - 9 byte tests
data.add(new Object[]{"7F0000000000000000"});
data.add(new Object[]{"7F0000000000000001"});
data.add(new Object[]{"7F0000000000000012"});
data.add(new Object[]{"7F0000000000001234"});
data.add(new Object[]{"7F000000000000FFFF"});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private String rawhex;
private byte buf[];
public MuxParserRead139Size_BadEncodingTest(String rawhex)
{
this.rawhex = rawhex;
this.buf = TypeUtil.fromHexString(rawhex);
}
@Test
public void testRead139EncodedSize()
{
System.err.printf("Running %s.%s - hex: %s%n",this.getClass().getName(),testname.getMethodName(),rawhex);
ByteBuffer bbuf = ByteBuffer.wrap(buf);
try
{
parser.read139EncodedSize(bbuf);
// unexpected path
Assert.fail("Should have failed with an invalid parse");
}
catch (MuxException e)
{
// expected path
Assert.assertThat(e.getMessage(),containsString("Invalid 1/3/9 length"));
}
}
}

View File

@ -1,99 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class MuxParserRead139Size_GoodTest
{
private static MuxParser parser = new MuxParser();
@Parameters
public static Collection<Object[]> data()
{
// Various good 1/3/9 encodings
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
data.add(new Object[]{"00", 0L});
data.add(new Object[]{"01", 1L});
data.add(new Object[]{"02", 2L});
data.add(new Object[]{"37", 55L});
data.add(new Object[]{"7D", 125L});
// extra bytes (not related to 1/3/9 size)
data.add(new Object[]{"37FF", 55L});
data.add(new Object[]{"0123456789", 0x01L});
// - 3 byte tests
data.add(new Object[]{"7E0080", 0x00_80L});
data.add(new Object[]{"7E00AB", 0x00_ABL});
data.add(new Object[]{"7E00FF", 0x00_FFL});
data.add(new Object[]{"7E3FFF", 0x3F_FFL});
// extra bytes (not related to 1/3/9 size)
data.add(new Object[]{"7E0123456789", 0x01_23L});
// - 9 byte tests
data.add(new Object[]{"7F000000000001FFFF", 0x00_00_01_FF_FFL});
data.add(new Object[]{"7F0000000000FFFFFF", 0x00_00_FF_FF_FFL});
data.add(new Object[]{"7F00000000FFFFFFFF", 0x00_FF_FF_FF_FFL});
data.add(new Object[]{"7F000000FFFFFFFFFF", 0xFF_FF_FF_FF_FFL});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private String rawhex;
private byte buf[];
private long expected;
public MuxParserRead139Size_GoodTest(String rawhex, long expected)
{
this.rawhex = rawhex;
this.buf = TypeUtil.fromHexString(rawhex);
this.expected = expected;
}
@Test
public void testRead139EncodedSize()
{
System.err.printf("Running %s.%s - hex: %s%n",this.getClass().getName(),testname.getMethodName(),rawhex);
ByteBuffer bbuf = ByteBuffer.wrap(buf);
long actual = parser.read139EncodedSize(bbuf);
Assert.assertThat("1/3/9 size from buffer [" + rawhex + "]",actual,is(expected));
}
}

View File

@ -1,106 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.containsString;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests of Invalid ChannelID parsing
*/
@RunWith(Parameterized.class)
public class MuxParserReadChannelId_BadEncodingTest
{
private static MuxParser parser = new MuxParser();
@Parameters
public static Collection<Object[]> data()
{
// Various Invalid Encoded Channel IDs.
// Violating "minimal number of bytes necessary" rule.
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
// all known 1 byte tests are valid
// - 2 byte tests
data.add(new Object[]{"8000"});
data.add(new Object[]{"8001"});
data.add(new Object[]{"807F"});
// extra bytes (not related to channelId)
data.add(new Object[]{"8023456789"});
// - 3 byte tests
data.add(new Object[]{"C00000"});
data.add(new Object[]{"C01234"});
data.add(new Object[]{"C03FFF"});
// - 3 byte tests
data.add(new Object[]{"E0000000"});
data.add(new Object[]{"E0000001"});
data.add(new Object[]{"E01FFFFF"});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private String rawhex;
private byte buf[];
public MuxParserReadChannelId_BadEncodingTest(String rawhex)
{
this.rawhex = rawhex;
this.buf = TypeUtil.fromHexString(rawhex);
}
@Test
public void testBadEncoding()
{
System.err.printf("Running %s.%s - hex: %s%n",this.getClass().getName(),testname.getMethodName(),rawhex);
ByteBuffer bbuf = ByteBuffer.wrap(buf);
try
{
parser.readChannelId(bbuf);
// unexpected path
Assert.fail("Should have failed with an invalid parse");
}
catch (MuxException e)
{
// expected path
Assert.assertThat(e.getMessage(),containsString("Invalid Channel ID"));
}
}
}

View File

@ -1,106 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux;
import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Tests of valid ChannelID parsing
*/
@RunWith(Parameterized.class)
public class MuxParserReadChannelId_GoodTest
{
private static MuxParser parser = new MuxParser();
@Parameters
public static Collection<Object[]> data()
{
// Various good Channel IDs
List<Object[]> data = new ArrayList<>();
// @formatter:off
// - 1 byte tests
data.add(new Object[]{"00", 0L});
data.add(new Object[]{"01", 1L});
data.add(new Object[]{"02", 2L});
data.add(new Object[]{"7F", 127L});
// extra bytes (not related to channelId)
data.add(new Object[]{"37FF", 55L});
data.add(new Object[]{"0123456789", 0x01L});
// - 2 byte tests
data.add(new Object[]{"8080", 0x00_80L});
data.add(new Object[]{"80FF", 0x00_FFL});
data.add(new Object[]{"BFFF", 0x3F_FFL});
// extra bytes (not related to channelId)
data.add(new Object[]{"8123456789", 0x01_23L});
// - 3 byte tests
data.add(new Object[]{"C0FFFF", 0x00_FF_FFL});
data.add(new Object[]{"DFFFFF", 0x1F_FF_FFL});
// extra bytes (not related to channelId)
data.add(new Object[]{"C123456789", 0x01_23_45L});
// - 3 byte tests
data.add(new Object[]{"E0FFFFFF", 0x00_FF_FF_FFL});
data.add(new Object[]{"FFFFFFFF", 0x1F_FF_FF_FFL});
// extra bytes (not related to channelId)
data.add(new Object[]{"E123456789", 0x01_23_45_67L});
// @formatter:on
return data;
}
@Rule
public TestName testname = new TestName();
private String rawhex;
private byte buf[];
private long expected;
public MuxParserReadChannelId_GoodTest(String rawhex, long expected)
{
this.rawhex = rawhex;
this.buf = TypeUtil.fromHexString(rawhex);
this.expected = expected;
}
@Test
public void testReadChannelId()
{
System.err.printf("Running %s.%s - hex: %s%n",this.getClass().getName(),testname.getMethodName(),rawhex);
ByteBuffer bbuf = ByteBuffer.wrap(buf);
long actual = parser.readChannelId(bbuf);
Assert.assertThat("Channel ID from buffer [" + rawhex + "]",actual,is(expected));
}
}

View File

@ -1,32 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
public class DummyMuxAddClient implements MuxAddClient
{
@Override
public WebSocketSession createSession(MuxAddChannelResponse response)
{
// TODO Auto-generated method stub
return null;
}
}

View File

@ -1,99 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import java.io.IOException;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.SessionListener;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
import org.eclipse.jetty.websocket.mux.MuxChannel;
import org.eclipse.jetty.websocket.mux.MuxException;
import org.eclipse.jetty.websocket.mux.Muxer;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import examples.echo.AdapterEchoSocket;
/**
* Dummy impl of MuxAddServer
*/
public class DummyMuxAddServer implements MuxAddServer
{
@SuppressWarnings("unused")
private static final Logger LOG = Log.getLogger(DummyMuxAddServer.class);
private AdapterEchoSocket echo;
private WebSocketPolicy policy;
private EventDriverFactory eventDriverFactory;
public DummyMuxAddServer()
{
this.policy = WebSocketPolicy.newServerPolicy();
this.eventDriverFactory = new EventDriverFactory(policy);
this.echo = new AdapterEchoSocket();
}
@Override
public UpgradeRequest getPhysicalHandshakeRequest()
{
// TODO Auto-generated method stub
return null;
}
@Override
public UpgradeResponse getPhysicalHandshakeResponse()
{
// TODO Auto-generated method stub
return null;
}
@Override
public void handshake(Muxer muxer, MuxChannel channel, UpgradeRequest request) throws MuxException, IOException
{
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 101 Switching Protocols\r\n");
response.append("Connection: upgrade\r\n");
// not meaningful (per Draft 08) hresp.append("Upgrade: websocket\r\n");
// not meaningful (per Draft 08) hresp.append("Sec-WebSocket-Accept: Kgo85/8KVE8YPONSeyhgL3GwqhI=\r\n");
response.append("\r\n");
EventDriver websocket = this.eventDriverFactory.wrap(echo);
WebSocketSession session = new WebSocketSession(request.getRequestURI(),websocket,channel, new SessionListener[0]);
UpgradeResponse uresponse = new UpgradeResponse();
uresponse.setAcceptedSubProtocol("echo");
session.setUpgradeResponse(uresponse);
channel.setSession(session);
channel.setSubProtocol("echo");
channel.onOpen();
session.open();
MuxAddChannelResponse addChannelResponse = new MuxAddChannelResponse();
addChannelResponse.setChannelId(channel.getChannelId());
addChannelResponse.setEncoding(MuxAddChannelResponse.IDENTITY_ENCODING);
addChannelResponse.setFailed(false);
addChannelResponse.setHandshake(response.toString());
muxer.output(addChannelResponse);
}
}

View File

@ -1,105 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.mux.MuxChannel;
import org.eclipse.jetty.websocket.mux.MuxDecoder;
import org.eclipse.jetty.websocket.mux.MuxEncoder;
import org.eclipse.jetty.websocket.mux.MuxOp;
import org.eclipse.jetty.websocket.mux.Muxer;
import org.eclipse.jetty.websocket.mux.helper.LocalWebSocketConnection;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
public class MuxerAddClientTest
{
@Rule
public TestName testname = new TestName();
@Test
@Ignore("Interrim, not functional yet")
public void testAddChannel_Client() throws Exception
{
// Client side physical socket
LocalWebSocketConnection physical = new LocalWebSocketConnection(testname);
physical.setPolicy(WebSocketPolicy.newClientPolicy());
physical.open();
// Server Reader
MuxDecoder serverRead = new MuxDecoder();
// Client side Muxer
Muxer muxer = new Muxer(physical);
DummyMuxAddClient addClient = new DummyMuxAddClient();
muxer.setAddClient(addClient);
muxer.setOutgoingFramesHandler(serverRead);
// Server Writer
MuxEncoder serverWrite = MuxEncoder.toIncoming(physical);
// Build AddChannelRequest handshake data
StringBuilder request = new StringBuilder();
request.append("GET /echo HTTP/1.1\r\n");
request.append("Host: localhost\r\n");
request.append("Upgrade: websocket\r\n");
request.append("Connection: Upgrade\r\n");
request.append("Sec-WebSocket-Key: ZDTIRU5vU9xOfkg8JAgN3A==\r\n");
request.append("Sec-WebSocket-Version: 13\r\n");
request.append("\r\n");
// Build AddChannelRequest
long channelId = 1L;
MuxAddChannelRequest req = new MuxAddChannelRequest();
req.setChannelId(channelId);
req.setEncoding((byte)0);
req.setHandshake(request.toString());
// Have client sent AddChannelRequest
MuxChannel channel = muxer.getChannel(channelId,true);
MuxEncoder clientWrite = MuxEncoder.toOutgoing(channel);
clientWrite.op(req);
// Have server read request
serverRead.assertHasOp(MuxOp.ADD_CHANNEL_REQUEST,1);
// prepare AddChannelResponse
StringBuilder response = new StringBuilder();
response.append("HTTP/1.1 101 Switching Protocols\r\n");
response.append("Upgrade: websocket\r\n");
response.append("Connection: upgrade\r\n");
response.append("Sec-WebSocket-Accept: Kgo85/8KVE8YPONSeyhgL3GwqhI=\r\n");
response.append("\r\n");
MuxAddChannelResponse resp = new MuxAddChannelResponse();
resp.setChannelId(channelId);
resp.setFailed(false);
resp.setEncoding((byte)0);
resp.setHandshake(resp.toString());
// Server writes add channel response
serverWrite.op(resp);
// TODO: handle the upgrade on client side.
}
}

View File

@ -1,106 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.add;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.mux.MuxDecoder;
import org.eclipse.jetty.websocket.mux.MuxEncoder;
import org.eclipse.jetty.websocket.mux.MuxOp;
import org.eclipse.jetty.websocket.mux.Muxer;
import org.eclipse.jetty.websocket.mux.helper.LocalWebSocketConnection;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelRequest;
import org.eclipse.jetty.websocket.mux.op.MuxAddChannelResponse;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
public class MuxerAddServerTest
{
@Rule
public TestName testname = new TestName();
@Test
@Ignore("Interrim, not functional yet")
public void testAddChannel_Server() throws Exception
{
// Server side physical connection
LocalWebSocketConnection physical = new LocalWebSocketConnection(testname);
physical.setPolicy(WebSocketPolicy.newServerPolicy());
physical.open();
// Client reader
MuxDecoder clientRead = new MuxDecoder();
// Build up server side muxer.
Muxer muxer = new Muxer(physical);
DummyMuxAddServer addServer = new DummyMuxAddServer();
muxer.setAddServer(addServer);
muxer.setOutgoingFramesHandler(clientRead);
// Wire up physical connection to forward incoming frames to muxer
physical.setNextIncomingFrames(muxer);
// Client simulator
// Can inject mux encapsulated frames into physical connection as if from
// physical connection.
MuxEncoder clientWrite = MuxEncoder.toIncoming(physical);
// Build AddChannelRequest handshake data
StringBuilder request = new StringBuilder();
request.append("GET /echo HTTP/1.1\r\n");
request.append("Host: localhost\r\n");
request.append("Upgrade: websocket\r\n");
request.append("Connection: Upgrade\r\n");
request.append("Sec-WebSocket-Key: ZDTIRU5vU9xOfkg8JAgN3A==\r\n");
request.append("Sec-WebSocket-Version: 13\r\n");
request.append("\r\n");
// Build AddChannelRequest
MuxAddChannelRequest req = new MuxAddChannelRequest();
req.setChannelId(1);
req.setEncoding((byte)0);
req.setHandshake(request.toString());
// Have client sent AddChannelRequest
clientWrite.op(req);
// Make sure client got AddChannelResponse
clientRead.assertHasOp(MuxOp.ADD_CHANNEL_RESPONSE,1);
MuxAddChannelResponse response = (MuxAddChannelResponse)clientRead.getOps().pop();
Assert.assertThat("AddChannelResponse.channelId",response.getChannelId(),is(1L));
Assert.assertThat("AddChannelResponse.failed",response.isFailed(),is(false));
Assert.assertThat("AddChannelResponse.handshake",response.getHandshake(),notNullValue());
Assert.assertThat("AddChannelResponse.handshakeSize",response.getHandshakeSize(),is(57L));
clientRead.reset();
// Send simple echo request
clientWrite.frame(1,new TextFrame().setPayload("Hello World"));
// Test for echo response (is there a user echo websocket connected to the sub-channel?)
clientRead.assertHasFrame(OpCode.TEXT,1L,1);
}
}

View File

@ -1,143 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.helper;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import java.util.LinkedList;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.junit.Assert;
public class IncomingFramesCapture implements IncomingFrames
{
private static final Logger LOG = Log.getLogger(IncomingFramesCapture.class);
private LinkedList<WebSocketFrame> frames = new LinkedList<>();
private LinkedList<Throwable> errors = new LinkedList<>();
public void assertErrorCount(int expectedCount)
{
Assert.assertThat("Captured error count",errors.size(),is(expectedCount));
}
public void assertFrameCount(int expectedCount)
{
Assert.assertThat("Captured frame count",frames.size(),is(expectedCount));
}
public void assertHasErrors(Class<? extends WebSocketException> errorType, int expectedCount)
{
Assert.assertThat(errorType.getSimpleName(),getErrorCount(errorType),is(expectedCount));
}
public void assertHasFrame(byte op)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),greaterThanOrEqualTo(1));
}
public void assertHasFrame(byte op, int expectedCount)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),is(expectedCount));
}
public void assertHasNoFrames()
{
Assert.assertThat("Has no frames",frames.size(),is(0));
}
public void assertNoErrors()
{
Assert.assertThat("Has no errors",errors.size(),is(0));
}
public void dump()
{
System.err.printf("Captured %d incoming frames%n",frames.size());
for (int i = 0; i < frames.size(); i++)
{
Frame frame = frames.get(i);
System.err.printf("[%3d] %s%n",i,frame);
System.err.printf(" %s%n",BufferUtil.toDetailString(frame.getPayload()));
}
}
public int getErrorCount(Class<? extends WebSocketException> errorType)
{
int count = 0;
for (Throwable error : errors)
{
if (errorType.isInstance(error))
{
count++;
}
}
return count;
}
public LinkedList<Throwable> getErrors()
{
return errors;
}
public int getFrameCount(byte op)
{
int count = 0;
for (WebSocketFrame frame : frames)
{
if (frame.getOpCode() == op)
{
count++;
}
}
return count;
}
public LinkedList<WebSocketFrame> getFrames()
{
return frames;
}
@Override
public void incomingError(Throwable e)
{
LOG.debug(e);
errors.add(e);
}
@Override
public void incomingFrame(Frame frame)
{
WebSocketFrame copy = WebSocketFrame.copy(frame);
frames.add(copy);
}
public int size()
{
return frames.size();
}
}

View File

@ -1,250 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.helper;
import java.net.InetSocketAddress;
import java.util.concurrent.Executor;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.SuspendToken;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.ConnectionState;
import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.io.IOState;
import org.eclipse.jetty.websocket.common.io.IOState.ConnectionStateListener;
import org.junit.rules.TestName;
public class LocalWebSocketConnection implements LogicalConnection, IncomingFrames, ConnectionStateListener
{
private static final Logger LOG = Log.getLogger(LocalWebSocketConnection.class);
private final String id;
private final ByteBufferPool bufferPool;
private final Executor executor;
private WebSocketPolicy policy = WebSocketPolicy.newServerPolicy();
private IncomingFrames incoming;
private IOState ioState = new IOState();
public LocalWebSocketConnection()
{
this("anon");
}
public LocalWebSocketConnection(String id)
{
this.id = id;
this.bufferPool = new MappedByteBufferPool();
this.executor = new ExecutorThreadPool();
this.ioState.addListener(this);
}
public LocalWebSocketConnection(TestName testname)
{
this(testname.getMethodName());
}
@Override
public Executor getExecutor()
{
return executor;
}
@Override
public void close()
{
close(StatusCode.NORMAL,null);
}
@Override
public void close(int statusCode, String reason)
{
LOG.debug("close({}, {})",statusCode,reason);
CloseInfo close = new CloseInfo(statusCode,reason);
ioState.onCloseLocal(close);
}
public void connect()
{
LOG.debug("connect()");
ioState.onConnected();
}
@Override
public void disconnect()
{
LOG.debug("disconnect()");
}
@Override
public ByteBufferPool getBufferPool()
{
return this.bufferPool;
}
@Override
public long getIdleTimeout()
{
return 0;
}
public IncomingFrames getIncoming()
{
return incoming;
}
@Override
public IOState getIOState()
{
return ioState;
}
@Override
public InetSocketAddress getLocalAddress()
{
return null;
}
@Override
public long getMaxIdleTimeout()
{
return 0;
}
@Override
public WebSocketPolicy getPolicy()
{
return policy;
}
@Override
public InetSocketAddress getRemoteAddress()
{
return null;
}
@Override
public WebSocketSession getSession()
{
return null;
}
@Override
public void incomingError(Throwable e)
{
incoming.incomingError(e);
}
@Override
public void incomingFrame(Frame frame)
{
incoming.incomingFrame(frame);
}
@Override
public boolean isOpen()
{
return getIOState().isOpen();
}
@Override
public boolean isReading()
{
return false;
}
@Override
public void onConnectionStateChange(ConnectionState state)
{
LOG.debug("Connection State Change: {}",state);
switch (state)
{
case CLOSED:
this.disconnect();
break;
case CLOSING:
if (ioState.wasRemoteCloseInitiated())
{
// send response close frame
CloseInfo close = ioState.getCloseInfo();
LOG.debug("write close frame: {}",close);
ioState.onCloseLocal(close);
}
default:
break;
}
}
public void open()
{
LOG.debug("open()");
ioState.onOpened();
}
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
}
@Override
public void resume()
{
}
@Override
public void setMaxIdleTimeout(long ms)
{
}
@Override
public void setNextIncomingFrames(IncomingFrames incoming)
{
this.incoming = incoming;
}
public void setPolicy(WebSocketPolicy policy)
{
this.policy = policy;
}
@Override
public void setSession(WebSocketSession session)
{
}
@Override
public SuspendToken suspend()
{
return null;
}
@Override
public String toString()
{
return String.format("%s[%s]",LocalWebSocketConnection.class.getSimpleName(),id);
}
}

View File

@ -1,51 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.helper;
import java.net.URI;
import org.eclipse.jetty.websocket.common.SessionListener;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.events.EventDriver;
import org.junit.rules.TestName;
public class LocalWebSocketSession extends WebSocketSession
{
private String id;
private OutgoingFramesCapture outgoingCapture;
public LocalWebSocketSession(TestName testname, EventDriver driver)
{
super(URI.create("ws://localhost/LocalWebSocketSesssion/" + testname.getMethodName()),driver,new LocalWebSocketConnection(testname), new SessionListener[0]);
this.id = testname.getMethodName();
outgoingCapture = new OutgoingFramesCapture();
setOutgoingHandler(outgoingCapture);
}
public OutgoingFramesCapture getOutgoingCapture()
{
return outgoingCapture;
}
@Override
public String toString()
{
return String.format("%s[%s]",LocalWebSocketSession.class.getSimpleName(),id);
}
}

View File

@ -1,97 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.helper;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import java.util.LinkedList;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
import org.eclipse.jetty.websocket.common.OpCode;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.junit.Assert;
public class OutgoingFramesCapture implements OutgoingFrames
{
private LinkedList<WebSocketFrame> frames = new LinkedList<>();
public void assertFrameCount(int expectedCount)
{
Assert.assertThat("Captured frame count",frames.size(),is(expectedCount));
}
public void assertHasFrame(byte op)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),greaterThanOrEqualTo(1));
}
public void assertHasFrame(byte op, int expectedCount)
{
Assert.assertThat(OpCode.name(op),getFrameCount(op),is(expectedCount));
}
public void assertHasNoFrames()
{
Assert.assertThat("Has no frames",frames.size(),is(0));
}
public void dump()
{
System.out.printf("Captured %d outgoing writes%n",frames.size());
for (int i = 0; i < frames.size(); i++)
{
Frame frame = frames.get(i);
System.out.printf("[%3d] %s%n",i,frame);
System.out.printf(" %s%n",BufferUtil.toDetailString(frame.getPayload()));
}
}
public int getFrameCount(byte op)
{
int count = 0;
for (WebSocketFrame frame : frames)
{
if (frame.getOpCode() == op)
{
count++;
}
}
return count;
}
public LinkedList<WebSocketFrame> getFrames()
{
return frames;
}
@Override
public void outgoingFrame(Frame frame, WriteCallback callback)
{
WebSocketFrame copy = WebSocketFrame.copy(frame);
frames.add(copy);
if (callback != null)
{
callback.writeSuccess();
}
}
}

View File

@ -1,78 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.mux.helper;
import java.nio.ByteBuffer;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.Parser;
public class UnitParser extends Parser
{
public UnitParser()
{
this(WebSocketPolicy.newServerPolicy());
}
public UnitParser(ByteBufferPool bufferPool, WebSocketPolicy policy)
{
super(policy,bufferPool);
}
public UnitParser(WebSocketPolicy policy)
{
this(new MappedByteBufferPool(),policy);
}
private void parsePartial(ByteBuffer buf, int numBytes)
{
int len = Math.min(numBytes,buf.remaining());
byte arr[] = new byte[len];
buf.get(arr,0,len);
this.parse(ByteBuffer.wrap(arr));
}
/**
* Parse a buffer, but do so in a quiet fashion, squelching stacktraces if encountered.
* <p>
* Use if you know the parse will cause an exception and just don't wnat to make the test console all noisy.
*/
public void parseQuietly(ByteBuffer buf)
{
try (StacklessLogging supress = new StacklessLogging(Parser.class))
{
parse(buf);
}
catch (Exception ignore)
{
/* ignore */
}
}
public void parseSlowly(ByteBuffer buf, int segmentSize)
{
while (buf.remaining() > 0)
{
parsePartial(buf,segmentSize);
}
}
}

View File

@ -1,7 +0,0 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.websocket.LEVEL=WARN
# org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.protocol.Parser.LEVEL=DEBUG
# org.eclipse.jetty.websocket.protocol.LEVEL=DEBUG
# org.eclipse.jetty.websocket.io.payload.LEVEL=DEBUG
# org.eclipse.jetty.websocket.core.extensions.compress.LEVEL=DEBUG