356823 correctly decode close codes. Send not utf-8 close code.

This commit is contained in:
Greg Wilkins 2011-09-07 11:37:46 +10:00
parent ba5af45d17
commit cf45e7b647
8 changed files with 166 additions and 38 deletions

View File

@ -177,7 +177,7 @@ public class HttpConnectionTest
response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+ response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+
"Host: localhost\n"+ "Host: localhost\n"+
"\015\012"); "\015\012");
checkContains(response,0,"pathInfo=/foo/bar?"); checkContains(response,0,"HTTP/1.1 400");
response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+ response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+
"Host: localhost\n"+ "Host: localhost\n"+

View File

@ -1,6 +1,7 @@
package org.eclipse.jetty.util; package org.eclipse.jetty.util;
import java.io.IOException; import java.io.IOException;
import java.util.IllegalFormatCodePointException;
public abstract class Utf8Appendable public abstract class Utf8Appendable
{ {
@ -69,6 +70,7 @@ public abstract class Utf8Appendable
_appendable.append('?'); _appendable.append('?');
_more=0; _more=0;
_bits=0; _bits=0;
throw new NotUtf8Exception();
} }
else else
_appendable.append((char)(0x7f&b)); _appendable.append((char)(0x7f&b));
@ -81,6 +83,7 @@ public abstract class Utf8Appendable
_appendable.append('?'); _appendable.append('?');
_more=0; _more=0;
_bits=0; _bits=0;
throw new NotUtf8Exception();
} }
else else
{ {
@ -116,7 +119,7 @@ public abstract class Utf8Appendable
} }
else else
{ {
throw new IllegalArgumentException("!utf8"); throw new NotUtf8Exception();
} }
} }
} }
@ -127,7 +130,7 @@ public abstract class Utf8Appendable
_appendable.append('?'); _appendable.append('?');
_more=0; _more=0;
_bits=0; _bits=0;
throw new IllegalArgumentException("!utf8"); throw new NotUtf8Exception();
} }
else else
{ {
@ -139,4 +142,12 @@ public abstract class Utf8Appendable
} }
} }
public static class NotUtf8Exception extends IllegalStateException
{
public NotUtf8Exception()
{
super("!UTF-8");
}
}
} }

View File

@ -60,7 +60,7 @@ public class Utf8StringBuffer extends Utf8Appendable
public StringBuffer getStringBuffer() public StringBuffer getStringBuffer()
{ {
if (_more!=0) if (_more!=0)
throw new IllegalStateException("!utf8"); throw new NotUtf8Exception();
return _buffer; return _buffer;
} }
@ -68,7 +68,7 @@ public class Utf8StringBuffer extends Utf8Appendable
public String toString() public String toString()
{ {
if (_more!=0) if (_more!=0)
throw new IllegalStateException("!utf8"); throw new NotUtf8Exception();
return _buffer.toString(); return _buffer.toString();
} }
} }

View File

@ -59,7 +59,7 @@ public class Utf8StringBuilder extends Utf8Appendable
public StringBuilder getStringBuilder() public StringBuilder getStringBuilder()
{ {
if (_more!=0) if (_more!=0)
throw new IllegalStateException("!utf8"); throw new NotUtf8Exception();
return _buffer; return _buffer;
} }
@ -67,7 +67,7 @@ public class Utf8StringBuilder extends Utf8Appendable
public String toString() public String toString()
{ {
if (_more!=0) if (_more!=0)
throw new IllegalStateException("!utf8"); throw new NotUtf8Exception();
return _buffer.toString(); return _buffer.toString();
} }
} }

View File

@ -50,7 +50,7 @@ public class Utf8StringBufferTest
} }
catch(IllegalStateException e) catch(IllegalStateException e)
{ {
assertTrue(e.toString().indexOf("!utf8")>=0); assertTrue(e.toString().indexOf("!UTF-8")>=0);
} }
} }
@ -64,8 +64,16 @@ public class Utf8StringBufferTest
bytes[4]=(byte)0x00; bytes[4]=(byte)0x00;
Utf8StringBuffer buffer = new Utf8StringBuffer(); Utf8StringBuffer buffer = new Utf8StringBuffer();
for (int i=0;i<bytes.length;i++) try
buffer.append(bytes[i]); {
for (int i=0;i<bytes.length;i++)
buffer.append(bytes[i]);
assertTrue(false);
}
catch(IllegalStateException e)
{
assertTrue(e.toString().indexOf("!UTF-8")>=0);
}
assertEquals("abc?",buffer.toString()); assertEquals("abc?",buffer.toString());
} }

View File

@ -33,7 +33,7 @@ public class Utf8StringBuilderTest
assertEquals(source, buffer.toString()); assertEquals(source, buffer.toString());
assertTrue(buffer.toString().endsWith("jetty")); assertTrue(buffer.toString().endsWith("jetty"));
} }
@Test @Test
public void testShort() public void testShort()
throws Exception throws Exception
@ -50,7 +50,7 @@ public class Utf8StringBuilderTest
} }
catch(IllegalStateException e) catch(IllegalStateException e)
{ {
assertTrue(e.toString().indexOf("!utf8")>=0); assertTrue(e.toString().indexOf("!UTF-8")>=0);
} }
} }
@ -64,9 +64,17 @@ public class Utf8StringBuilderTest
bytes[4]=(byte)0x00; bytes[4]=(byte)0x00;
Utf8StringBuilder buffer = new Utf8StringBuilder(); Utf8StringBuilder buffer = new Utf8StringBuilder();
for (int i=0;i<bytes.length;i++) try
buffer.append(bytes[i]); {
assertEquals("abc?",buffer.toString()); for (int i = 0; i < bytes.length; i++)
buffer.append(bytes[i]);
assertTrue(false);
}
catch(IllegalStateException e)
{
assertTrue(e.toString().indexOf("!UTF-8")>=0);
}
assertEquals("abc?", buffer.toString());
} }

View File

@ -30,6 +30,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8Appendable;
import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -83,7 +84,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
private final IdleCheck _idle; private final IdleCheck _idle;
private final List<Extension> _extensions; private final List<Extension> _extensions;
private final WebSocketParserD13 _parser; private final WebSocketParserD13 _parser;
private final WebSocketParser.FrameHandler _inbound;
private final WebSocketGeneratorD13 _generator; private final WebSocketGeneratorD13 _generator;
private final WebSocketGenerator _outbound; private final WebSocketGenerator _outbound;
private final WebSocket _webSocket; private final WebSocket _webSocket;
@ -113,8 +113,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
} }
} }
private final WebSocketParser.FrameHandler _frameHandler= new WSFrameHandler();
private final WebSocket.FrameConnection _connection = new WSFrameConnection(); private final WebSocket.FrameConnection _connection = new WSFrameConnection();
@ -147,6 +145,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
_generator = new WebSocketGeneratorD13(buffers, _endp,maskgen); _generator = new WebSocketGeneratorD13(buffers, _endp,maskgen);
_extensions=extensions; _extensions=extensions;
WebSocketParser.FrameHandler frameHandler = new WSFrameHandler();
if (_extensions!=null) if (_extensions!=null)
{ {
int e=0; int e=0;
@ -154,16 +153,16 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{ {
extension.bind( extension.bind(
_connection, _connection,
e==extensions.size()-1?_frameHandler:extensions.get(e+1), e==extensions.size()-1? frameHandler :extensions.get(e+1),
e==0?_generator:extensions.get(e-1)); e==0?_generator:extensions.get(e-1));
e++; e++;
} }
} }
_outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1); _outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1);
_inbound=(_extensions==null||_extensions.size()==0)?_frameHandler:extensions.get(0); WebSocketParser.FrameHandler inbound = (_extensions == null || _extensions.size() == 0) ? frameHandler : extensions.get(0);
_parser = new WebSocketParserD13(buffers, endpoint,_inbound,maskgen==null); _parser = new WebSocketParserD13(buffers, endpoint, inbound,maskgen==null);
_protocol=protocol; _protocol=protocol;
@ -301,14 +300,14 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{ {
LOG.debug("ClosedIn {} {}",this,message); LOG.debug("ClosedIn {} {}",this,message);
final boolean closedOut; final boolean close;
final boolean closed; final boolean tell_app;
synchronized (this) synchronized (this)
{ {
closedOut=_closedOut; close=_closedOut;
_closedIn=true; _closedIn=true;
closed=_closeCode==0; tell_app=_closeCode==0;
if (closed) if (tell_app)
{ {
_closeCode=code; _closeCode=code;
_closeMessage=message; _closeMessage=message;
@ -317,14 +316,14 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
try try
{ {
if (closed) if (tell_app)
_webSocket.onClose(code,message); _webSocket.onClose(code,message);
} }
finally finally
{ {
try try
{ {
if (closedOut) if (close)
_endp.close(); _endp.close();
else else
closeOut(code,message); closeOut(code,message);
@ -342,13 +341,15 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
LOG.debug("ClosedOut {} {}",this,message); LOG.debug("ClosedOut {} {}",this,message);
final boolean close; final boolean close;
final boolean closed; final boolean tell_app;
final boolean send_close;
synchronized (this) synchronized (this)
{ {
close=_closedIn || _closedOut; close=_closedIn;
send_close=!_closedOut;
_closedOut=true; _closedOut=true;
closed=_closeCode==0; tell_app=_closeCode==0;
if (closed) if (tell_app)
{ {
_closeCode=code; _closeCode=code;
_closeMessage=message; _closeMessage=message;
@ -357,16 +358,14 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
try try
{ {
if (closed) if (tell_app)
_webSocket.onClose(code,message); _webSocket.onClose(code,message);
} }
finally finally
{ {
try try
{ {
if (close) if (send_close)
_endp.close();
else
{ {
if (code<=0) if (code<=0)
code=WebSocketConnectionD13.CLOSE_NORMAL; code=WebSocketConnectionD13.CLOSE_NORMAL;
@ -374,8 +373,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
bytes[0]=(byte)(code/0x100); bytes[0]=(byte)(code/0x100);
bytes[1]=(byte)(code%0x100); bytes[1]=(byte)(code%0x100);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length);
_outbound.flush();
if (close)
_endp.shutdownOutput();
} }
_outbound.flush(); else if (close)
_endp.close();
} }
catch(IOException e) catch(IOException e)
@ -711,7 +714,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
String message=null; String message=null;
if (buffer.length()>=2) if (buffer.length()>=2)
{ {
code=buffer.array()[buffer.getIndex()]*0x100+buffer.array()[buffer.getIndex()+1]; code=(0xff&buffer.array()[buffer.getIndex()])*0x100+(0xff&buffer.array()[buffer.getIndex()+1]);
if (buffer.length()>2) if (buffer.length()>2)
message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8); message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8);
} }
@ -779,6 +782,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
} }
} }
} }
catch(Utf8Appendable.NotUtf8Exception notUtf8)
{
LOG.warn("{} for {}",notUtf8,_endp);
LOG.debug(notUtf8);
_connection.close(WebSocketConnectionD13.CLOSE_NOT_UTF8,"Invalid UTF-8");
}
catch(ThreadDeath th) catch(ThreadDeath th)
{ {
throw th; throw th;

View File

@ -909,6 +909,98 @@ public class WebSocketMessageD13Test
lookFor("Message size > 15",input); lookFor("Message size > 15",input);
} }
@Test
public void testCloseCode() throws Exception
{
Socket socket = new Socket("localhost", __connector.getLocalPort());
OutputStream output = socket.getOutputStream();
output.write(
("GET /chat HTTP/1.1\r\n"+
"Host: server.example.com\r\n"+
"Upgrade: websocket\r\n"+
"Connection: Upgrade\r\n"+
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
"Sec-WebSocket-Origin: http://example.com\r\n"+
"Sec-WebSocket-Protocol: chat\r\n" +
"Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
socket.setSoTimeout(100000);
InputStream input = socket.getInputStream();
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
skipTo("Sec-WebSocket-Accept: ",input);
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
skipTo("\r\n\r\n",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.getConnection().setMaxBinaryMessageSize(15);
output.write(0x88);
output.write(0x82);
output.write(0x00);
output.write(0x00);
output.write(0x00);
output.write(0x00);
output.write(0x81);
output.write(0xFF);
output.flush();
assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read());
assertEquals(2,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(0x81FF,code);
}
@Test
public void testNotUTF8() throws Exception
{
Socket socket = new Socket("localhost", __connector.getLocalPort());
OutputStream output = socket.getOutputStream();
output.write(
("GET /chat HTTP/1.1\r\n"+
"Host: server.example.com\r\n"+
"Upgrade: websocket\r\n"+
"Connection: Upgrade\r\n"+
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+
"Sec-WebSocket-Origin: http://example.com\r\n"+
"Sec-WebSocket-Protocol: chat\r\n" +
"Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+
"\r\n").getBytes("ISO-8859-1"));
output.flush();
socket.setSoTimeout(100000);
InputStream input = socket.getInputStream();
lookFor("HTTP/1.1 101 Switching Protocols\r\n",input);
skipTo("Sec-WebSocket-Accept: ",input);
lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input);
skipTo("\r\n\r\n",input);
assertTrue(__serverWebSocket.awaitConnected(1000));
assertNotNull(__serverWebSocket.connection);
__serverWebSocket.getConnection().setMaxBinaryMessageSize(15);
output.write(0x81);
output.write(0x82);
output.write(0x00);
output.write(0x00);
output.write(0x00);
output.write(0x00);
output.write(0xc3);
output.write(0x28);
output.flush();
assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read());
assertEquals(15,input.read());
int code=(0xff&input.read())*0x100+(0xff&input.read());
assertEquals(WebSocketConnectionD13.CLOSE_NOT_UTF8,code);
lookFor("Invalid UTF-8",input);
}
@Test @Test
public void testMaxBinarySize2() throws Exception public void testMaxBinarySize2() throws Exception