427690 - Remove Mux Extension and related support.
This commit is contained in:
parent
66a19bd646
commit
b37c68db3b
|
@ -19,7 +19,6 @@
|
||||||
<module>websocket-client</module>
|
<module>websocket-client</module>
|
||||||
<module>websocket-server</module>
|
<module>websocket-server</module>
|
||||||
<module>websocket-servlet</module>
|
<module>websocket-servlet</module>
|
||||||
<module>websocket-mux-extension</module>
|
|
||||||
<module>javax-websocket-client-impl</module>
|
<module>javax-websocket-client-impl</module>
|
||||||
<module>javax-websocket-server-impl</module>
|
<module>javax-websocket-server-impl</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
|
@ -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>
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
org.eclipse.jetty.websocket.mux.client.MuxClientExtension
|
|
|
@ -1 +0,0 @@
|
||||||
org.eclipse.jetty.websocket.mux.client.MuxServerExtension
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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.
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
Loading…
Reference in New Issue