324369 Improved handling of multiple versions of draft-ietf-hybi-thewebsocketprotocol

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2342 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2010-10-13 00:34:30 +00:00
parent fd90e82e64
commit de178f18c6
8 changed files with 417 additions and 369 deletions

View File

@ -2,6 +2,7 @@ jetty-7.2.0.RC1-SNAPSHOT
+ 289540 added javadoc into distribution + 289540 added javadoc into distribution
+ 297154 add source distribution artifact + 297154 add source distribution artifact
+ 323985 Xmlconfiguration pulls start.jar config properties + 323985 Xmlconfiguration pulls start.jar config properties
+ 324369 Improved handling of multiple versions of draft-ietf-hybi-thewebsocketprotocol
+ 326734 Configure Digest maxNonceAge with Security handler init param + 326734 Configure Digest maxNonceAge with Security handler init param
+ 327109 Fixed AJP handling of empty packets + 327109 Fixed AJP handling of empty packets
+ 327183 Allow better configurability of HttpClient for TLS/SSL + 327183 Allow better configurability of HttpClient for TLS/SSL

View File

@ -29,7 +29,7 @@ import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketConnection; import org.eclipse.jetty.websocket.WebSocketConnectionD00;
import org.eclipse.jetty.websocket.WebSocketHandler; import org.eclipse.jetty.websocket.WebSocketHandler;
/** /**
@ -123,7 +123,7 @@ public class WebSocketUpgradeTest extends TestCase
protected Connection onSwitchProtocol(EndPoint endp) throws IOException protected Connection onSwitchProtocol(EndPoint endp) throws IOException
{ {
waitFor(3); waitFor(3);
WebSocketConnection connection = new WebSocketConnection(clientWS,endp,0); WebSocketConnectionD00 connection = new WebSocketConnectionD00(clientWS,endp,0);
_results.add("onSwitchProtocol"); _results.add("onSwitchProtocol");
_results.add(connection); _results.add(connection);
@ -164,7 +164,7 @@ public class WebSocketUpgradeTest extends TestCase
assertEquals(new Integer(101), _results.poll(1,TimeUnit.SECONDS)); assertEquals(new Integer(101), _results.poll(1,TimeUnit.SECONDS));
assertEquals("onSwitchProtocol", _results.poll(1,TimeUnit.SECONDS)); assertEquals("onSwitchProtocol", _results.poll(1,TimeUnit.SECONDS));
WebSocketConnection client_conn=(WebSocketConnection)_results.poll(1,TimeUnit.SECONDS); WebSocketConnectionD00 client_conn=(WebSocketConnectionD00)_results.poll(1,TimeUnit.SECONDS);
assertEquals("clientWS.onConnect", _results.poll(1,TimeUnit.SECONDS)); assertEquals("clientWS.onConnect", _results.poll(1,TimeUnit.SECONDS));
assertEquals(client_conn, _results.poll(1,TimeUnit.SECONDS)); assertEquals(client_conn, _results.poll(1,TimeUnit.SECONDS));

View File

@ -22,12 +22,12 @@ final class FrameHandlerD1 implements WebSocketParser.FrameHandler
public final static byte PING=1; public final static byte PING=1;
public final static byte PONG=1; public final static byte PONG=1;
final WebSocketConnection _connection; final WebSocketConnectionD00 _connection;
final WebSocket _websocket; final WebSocket _websocket;
final Utf8StringBuilder _utf8 = new Utf8StringBuilder(); final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
boolean _fragmented=false; boolean _fragmented=false;
FrameHandlerD1(WebSocketConnection connection, WebSocket websocket) FrameHandlerD1(WebSocketConnectionD00 connection, WebSocket websocket)
{ {
_connection=connection; _connection=connection;
_websocket=websocket; _websocket=websocket;

View File

@ -1,328 +1,17 @@
// ========================================================================
// Copyright (c) 2010 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; package org.eclipse.jetty.websocket;
import java.io.IOException; import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.eclipse.jetty.io.AsyncEndPoint; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.log.Log;
public class WebSocketConnection implements Connection, WebSocket.Outbound public interface WebSocketConnection extends Connection, WebSocket.Outbound
{ {
final IdleCheck _idle; void fillBuffersFrom(Buffer buffer);
final EndPoint _endp;
final WebSocketParser _parser;
final WebSocketGenerator _generator;
final long _timestamp;
final WebSocket _websocket;
String _key1;
String _key2;
ByteArrayBuffer _hixieBytes;
public WebSocketConnection(WebSocket websocket, EndPoint endpoint,int draft)
throws IOException
{
this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft);
}
public WebSocketConnection(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft) void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException;
throws IOException }
{
// TODO - can we use the endpoint idle mechanism?
if (endpoint instanceof AsyncEndPoint)
((AsyncEndPoint)endpoint).cancelIdle();
_endp = endpoint;
_endp.setMaxIdleTime(maxIdleTime);
_timestamp = timestamp;
_websocket = websocket;
// Select the parser/generators to use
switch(draft)
{
case 1:
_generator = new WebSocketGeneratorD01(buffers, _endp);
_parser = new WebSocketParserD01(buffers, endpoint, new FrameHandlerD1(this,_websocket));
break;
default:
_generator = new WebSocketGeneratorD00(buffers, _endp);
_parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
}
// TODO should these be AsyncEndPoint checks/calls?
if (_endp instanceof SelectChannelEndPoint)
{
final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
scep.cancelIdle();
_idle=new IdleCheck()
{
public void access(EndPoint endp)
{
scep.scheduleIdle();
}
};
scep.scheduleIdle();
}
else
{
_idle = new IdleCheck()
{
public void access(EndPoint endp)
{}
};
}
}
public void setHixieKeys(String key1,String key2)
{
_key1=key1;
_key2=key2;
_hixieBytes=new IndirectNIOBuffer(16);
}
public Connection handle() throws IOException
{
try
{
// handle stupid hixie random bytes
if (_hixieBytes!=null)
{
// take any available bytes from the parser buffer, which may have already been read
Buffer buffer=_parser.getBuffer();
if (buffer!=null && buffer.length()>0)
{
int l=buffer.length();
if (l>(8-_hixieBytes.length()))
l=8-_hixieBytes.length();
_hixieBytes.put(buffer.peek(buffer.getIndex(),l));
buffer.skip(l);
}
// while we are not blocked
while(_endp.isOpen())
{
// do we now have enough
if (_hixieBytes.length()==8)
{
// we have the silly random bytes
// so let's work out the stupid 16 byte reply.
doTheHixieHixieShake();
_endp.flush(_hixieBytes);
_hixieBytes=null;
_endp.flush();
break;
}
// no, then let's fill
int filled=_endp.fill(_hixieBytes);
if (filled<0)
{
_endp.close();
break;
}
}
_websocket.onConnect(this);
return this;
}
// handle the framing protocol
boolean progress=true;
while (progress)
{
int flushed=_generator.flush();
int filled=_parser.parseNext();
progress = flushed>0 || filled>0;
if (filled<0 || flushed<0)
{
_endp.close();
break;
}
}
}
catch(IOException e)
{
try
{
_endp.close();
}
catch(IOException e2)
{
Log.ignore(e2);
}
throw e;
}
finally
{
if (_endp.isOpen())
{
_idle.access(_endp);
checkWriteable();
}
}
return this;
}
private void doTheHixieHixieShake()
{
byte[] result=WebSocketConnection.doTheHixieHixieShake(
WebSocketConnection.hixieCrypt(_key1),
WebSocketConnection.hixieCrypt(_key2),
_hixieBytes.asArray());
_hixieBytes.clear();
_hixieBytes.put(result);
}
public boolean isOpen()
{
return _endp!=null&&_endp.isOpen();
}
public boolean isIdle()
{
return _parser.isBufferEmpty() && _generator.isBufferEmpty();
}
public boolean isSuspended()
{
return false;
}
public void closed()
{
_websocket.onDisconnect();
}
public long getTimeStamp()
{
return _timestamp;
}
public void sendMessage(String content) throws IOException
{
sendMessage(WebSocket.SENTINEL_FRAME,content);
}
public void sendMessage(byte frame, String content) throws IOException
{
_generator.addFrame(frame,content,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException
{
_generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException
{
_generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
public void disconnect()
{
try
{
_generator.flush(_endp.getMaxIdleTime());
_endp.close();
}
catch(IOException e)
{
Log.ignore(e);
}
}
public void fill(Buffer buffer)
{
_parser.fill(buffer);
}
private void checkWriteable()
{
if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
((AsyncEndPoint)_endp).scheduleWrite();
}
/* ------------------------------------------------------------ */
static long hixieCrypt(String key)
{
// Don't ask me what all this is about.
// I think it's pretend secret stuff, kind of
// like talking in pig latin!
long number=0;
int spaces=0;
for (char c : key.toCharArray())
{
if (Character.isDigit(c))
number=number*10+(c-'0');
else if (c==' ')
spaces++;
}
return number/spaces;
}
public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte [] fodder = new byte[16];
fodder[0]=(byte)(0xff&(key1>>24));
fodder[1]=(byte)(0xff&(key1>>16));
fodder[2]=(byte)(0xff&(key1>>8));
fodder[3]=(byte)(0xff&key1);
fodder[4]=(byte)(0xff&(key2>>24));
fodder[5]=(byte)(0xff&(key2>>16));
fodder[6]=(byte)(0xff&(key2>>8));
fodder[7]=(byte)(0xff&key2);
for (int i=0;i<8;i++)
fodder[8+i]=key3[i];
md.update(fodder);
byte[] result=md.digest();
return result;
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
}
private interface IdleCheck
{
void access(EndPoint endp);
}
}

View File

@ -0,0 +1,383 @@
// ========================================================================
// Copyright (c) 2010 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;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.log.Log;
public class WebSocketConnectionD00 implements WebSocketConnection
{
final IdleCheck _idle;
final EndPoint _endp;
final WebSocketParser _parser;
final WebSocketGenerator _generator;
final long _timestamp;
final WebSocket _websocket;
String _key1;
String _key2;
ByteArrayBuffer _hixieBytes;
public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint,int draft)
throws IOException
{
this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft);
}
public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft)
throws IOException
{
// TODO - can we use the endpoint idle mechanism?
if (endpoint instanceof AsyncEndPoint)
((AsyncEndPoint)endpoint).cancelIdle();
_endp = endpoint;
_endp.setMaxIdleTime(maxIdleTime);
_timestamp = timestamp;
_websocket = websocket;
// Select the parser/generators to use
switch(draft)
{
case 1:
_generator = new WebSocketGeneratorD01(buffers, _endp);
_parser = new WebSocketParserD01(buffers, endpoint, new FrameHandlerD1(this,_websocket));
break;
default:
_generator = new WebSocketGeneratorD00(buffers, _endp);
_parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
}
// TODO should these be AsyncEndPoint checks/calls?
if (_endp instanceof SelectChannelEndPoint)
{
final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
scep.cancelIdle();
_idle=new IdleCheck()
{
public void access(EndPoint endp)
{
scep.scheduleIdle();
}
};
scep.scheduleIdle();
}
else
{
_idle = new IdleCheck()
{
public void access(EndPoint endp)
{}
};
}
}
public void setHixieKeys(String key1,String key2)
{
_key1=key1;
_key2=key2;
_hixieBytes=new IndirectNIOBuffer(16);
}
public Connection handle() throws IOException
{
try
{
// handle stupid hixie random bytes
if (_hixieBytes!=null)
{
// take any available bytes from the parser buffer, which may have already been read
Buffer buffer=_parser.getBuffer();
if (buffer!=null && buffer.length()>0)
{
int l=buffer.length();
if (l>(8-_hixieBytes.length()))
l=8-_hixieBytes.length();
_hixieBytes.put(buffer.peek(buffer.getIndex(),l));
buffer.skip(l);
}
// while we are not blocked
while(_endp.isOpen())
{
// do we now have enough
if (_hixieBytes.length()==8)
{
// we have the silly random bytes
// so let's work out the stupid 16 byte reply.
doTheHixieHixieShake();
_endp.flush(_hixieBytes);
_hixieBytes=null;
_endp.flush();
break;
}
// no, then let's fill
int filled=_endp.fill(_hixieBytes);
if (filled<0)
{
_endp.close();
break;
}
}
_websocket.onConnect(this);
return this;
}
// handle the framing protocol
boolean progress=true;
while (progress)
{
int flushed=_generator.flush();
int filled=_parser.parseNext();
progress = flushed>0 || filled>0;
if (filled<0 || flushed<0)
{
_endp.close();
break;
}
}
}
catch(IOException e)
{
try
{
_endp.close();
}
catch(IOException e2)
{
Log.ignore(e2);
}
throw e;
}
finally
{
if (_endp.isOpen())
{
_idle.access(_endp);
checkWriteable();
}
}
return this;
}
private void doTheHixieHixieShake()
{
byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
WebSocketConnectionD00.hixieCrypt(_key1),
WebSocketConnectionD00.hixieCrypt(_key2),
_hixieBytes.asArray());
_hixieBytes.clear();
_hixieBytes.put(result);
}
public boolean isOpen()
{
return _endp!=null&&_endp.isOpen();
}
public boolean isIdle()
{
return _parser.isBufferEmpty() && _generator.isBufferEmpty();
}
public boolean isSuspended()
{
return false;
}
public void closed()
{
_websocket.onDisconnect();
}
public long getTimeStamp()
{
return _timestamp;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(java.lang.String)
*/
public void sendMessage(String content) throws IOException
{
sendMessage(WebSocket.SENTINEL_FRAME,content);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, java.lang.String)
*/
public void sendMessage(byte frame, String content) throws IOException
{
_generator.addFrame(frame,content,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.websocket.WebSocketConnection#sendMessage(byte, byte[], int, int)
*/
public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException
{
_generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.websocket.WebSocketConnection#sendFragment(boolean, byte, byte[], int, int)
*/
public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException
{
_generator.addFragment(more,opcode,content,offset,length,_endp.getMaxIdleTime());
_generator.flush();
checkWriteable();
_idle.access(_endp);
}
public void disconnect()
{
try
{
_generator.flush(_endp.getMaxIdleTime());
_endp.close();
}
catch(IOException e)
{
Log.ignore(e);
}
}
public void fillBuffersFrom(Buffer buffer)
{
_parser.fill(buffer);
}
private void checkWriteable()
{
if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
((AsyncEndPoint)_endp).scheduleWrite();
}
/* ------------------------------------------------------------ */
static long hixieCrypt(String key)
{
// Don't ask me what all this is about.
// I think it's pretend secret stuff, kind of
// like talking in pig latin!
long number=0;
int spaces=0;
for (char c : key.toCharArray())
{
if (Character.isDigit(c))
number=number*10+(c-'0');
else if (c==' ')
spaces++;
}
return number/spaces;
}
public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
{
try
{
MessageDigest md = MessageDigest.getInstance("MD5");
byte [] fodder = new byte[16];
fodder[0]=(byte)(0xff&(key1>>24));
fodder[1]=(byte)(0xff&(key1>>16));
fodder[2]=(byte)(0xff&(key1>>8));
fodder[3]=(byte)(0xff&key1);
fodder[4]=(byte)(0xff&(key2>>24));
fodder[5]=(byte)(0xff&(key2>>16));
fodder[6]=(byte)(0xff&(key2>>8));
fodder[7]=(byte)(0xff&key2);
for (int i=0;i<8;i++)
fodder[8+i]=key3[i];
md.update(fodder);
byte[] result=md.digest();
return result;
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException(e);
}
}
private interface IdleCheck
{
void access(EndPoint endp);
}
public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException
{
String uri=request.getRequestURI();
String query=request.getQueryString();
if (query!=null && query.length()>0)
uri+="?"+query;
String host=request.getHeader("Host");
String key1 = request.getHeader("Sec-WebSocket-Key1");
if (key1!=null)
{
String key2 = request.getHeader("Sec-WebSocket-Key2");
setHixieKeys(key1,key2);
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Origin",origin);
response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (subprotocol!=null)
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
response.sendError(101,"WebSocket Protocol Handshake");
}
else
{
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("WebSocket-Origin",origin);
response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (subprotocol!=null)
response.addHeader("WebSocket-Protocol",subprotocol);
response.sendError(101,"Web Socket Protocol Handshake");
response.flushBuffer();
_websocket.onConnect(this);
}
}
}

View File

@ -89,11 +89,11 @@ public class WebSocketFactory
* @param response The response to upgrade * @param response The response to upgrade
* @param websocket The websocket handler implementation to use * @param websocket The websocket handler implementation to use
* @param origin The origin of the websocket connection * @param origin The origin of the websocket connection
* @param protocol The protocol * @param subprotocol The protocol
* @throws UpgradeConnectionException Thrown to upgrade the connection * @throws UpgradeConnectionException Thrown to upgrade the connection
* @throws IOException * @throws IOException
*/ */
public void upgrade(HttpServletRequest request,HttpServletResponse response, WebSocket websocket, String origin, String protocol) public void upgrade(HttpServletRequest request,HttpServletResponse response, WebSocket websocket, String origin, String subprotocol)
throws IOException throws IOException
{ {
if (!"WebSocket".equals(request.getHeader("Upgrade"))) if (!"WebSocket".equals(request.getHeader("Upgrade")))
@ -104,48 +104,23 @@ public class WebSocketFactory
int draft=request.getIntHeader("Sec-WebSocket-Draft"); int draft=request.getIntHeader("Sec-WebSocket-Draft");
HttpConnection http = HttpConnection.getCurrentConnection(); HttpConnection http = HttpConnection.getCurrentConnection();
ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint(); ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint();
WebSocketConnection connection = new WebSocketConnection(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
String uri=request.getRequestURI(); final WebSocketConnection connection;
String query=request.getQueryString(); switch(draft)
if (query!=null && query.length()>0)
uri+="?"+query;
String host=request.getHeader("Host");
String key1 = request.getHeader("Sec-WebSocket-Key1");
if (key1!=null)
{ {
String key2 = request.getHeader("Sec-WebSocket-Key2"); default:
connection.setHixieKeys(key1,key2); connection=new WebSocketConnectionD00(websocket,endp,_buffers,http.getTimeStamp(), _maxIdleTime,draft);
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Origin",origin);
response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (protocol!=null)
response.addHeader("Sec-WebSocket-Protocol",protocol);
response.sendError(101,"WebSocket Protocol Handshake");
}
else
{
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("WebSocket-Origin",origin);
response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (protocol!=null)
response.addHeader("WebSocket-Protocol",protocol);
response.sendError(101,"Web Socket Protocol Handshake");
} }
// Let the connection finish processing the handshake
connection.handshake(request,response, origin, subprotocol);
response.flushBuffer(); response.flushBuffer();
connection.fill(((HttpParser)http.getParser()).getHeaderBuffer()); // Give the connection any unused data from the HTTP connection.
connection.fill(((HttpParser)http.getParser()).getBodyBuffer()); connection.fillBuffersFrom(((HttpParser)http.getParser()).getHeaderBuffer());
connection.fillBuffersFrom(((HttpParser)http.getParser()).getBodyBuffer());
// connect here for -75, but in connection for -76 onwards // Tell jetty about the new connection
if (key1==null)
websocket.onConnect(connection);
request.setAttribute("org.eclipse.jetty.io.Connection",connection); request.setAttribute("org.eclipse.jetty.io.Connection",connection);
} }
} }

View File

@ -97,15 +97,15 @@ public abstract class WebSocketHandler extends HandlerWrapper
{ {
if ("WebSocket".equals(request.getHeader("Upgrade"))) if ("WebSocket".equals(request.getHeader("Upgrade")))
{ {
String protocol=request.getHeader(request.getHeader("Sec-WebSocket-Key1")!=null?"Sec-WebSocket-Protocol":"WebSocket-Protocol"); String subprotocol=request.getHeader(request.getHeader("Sec-WebSocket-Key1")!=null?"Sec-WebSocket-Protocol":"WebSocket-Protocol");
WebSocket websocket=doWebSocketConnect(request,protocol); WebSocket websocket=doWebSocketConnect(request,subprotocol);
String host=request.getHeader("Host"); String host=request.getHeader("Host");
String origin=request.getHeader("Origin"); String origin=request.getHeader("Origin");
origin=checkOrigin(request,host,origin); origin=checkOrigin(request,host,origin);
if (websocket!=null) if (websocket!=null)
_websocket.upgrade(request,response,websocket,origin,protocol); _websocket.upgrade(request,response,websocket,origin,subprotocol);
else else
response.sendError(503); response.sendError(503);
} }

View File

@ -57,8 +57,8 @@ public class WebSocketTest
@Test @Test
public void testHixieCrypt() throws Exception public void testHixieCrypt() throws Exception
{ {
assertEquals(155712099,WebSocketConnection.hixieCrypt("18x 6]8vM;54 *(5: { U1]8 z [ 8")); assertEquals(155712099,WebSocketConnectionD00.hixieCrypt("18x 6]8vM;54 *(5: { U1]8 z [ 8"));
assertEquals(173347027,WebSocketConnection.hixieCrypt("1_ tx7X d < nw 334J702) 7]o}` 0")); assertEquals(173347027,WebSocketConnectionD00.hixieCrypt("1_ tx7X d < nw 334J702) 7]o}` 0"));
} }
@Test @Test
@ -69,12 +69,12 @@ public class WebSocketTest
byte[] expected; byte[] expected;
expected=md.digest(TypeUtil.fromHexString("00000000000000000000000000000000")); expected=md.digest(TypeUtil.fromHexString("00000000000000000000000000000000"));
result=WebSocketConnection.doTheHixieHixieShake( result=WebSocketConnectionD00.doTheHixieHixieShake(
0 ,0, new byte[8]); 0 ,0, new byte[8]);
assertEquals(TypeUtil.toHexString(expected),TypeUtil.toHexString(result)); assertEquals(TypeUtil.toHexString(expected),TypeUtil.toHexString(result));
expected=md.digest(TypeUtil.fromHexString("01020304050607080000000000000000")); expected=md.digest(TypeUtil.fromHexString("01020304050607080000000000000000"));
result=WebSocketConnection.doTheHixieHixieShake( result=WebSocketConnectionD00.doTheHixieHixieShake(
0x01020304, 0x01020304,
0x05060708, 0x05060708,
new byte[8]); new byte[8]);
@ -83,7 +83,7 @@ public class WebSocketTest
byte[] random = new byte[8]; byte[] random = new byte[8];
for (int i=0;i<8;i++) for (int i=0;i<8;i++)
random[i]=(byte)(0xff&"Tm[K T2u".charAt(i)); random[i]=(byte)(0xff&"Tm[K T2u".charAt(i));
result=WebSocketConnection.doTheHixieHixieShake( result=WebSocketConnectionD00.doTheHixieHixieShake(
155712099,173347027,random); 155712099,173347027,random);
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();