Issue #272 - Read/Parse exceptions should flow out, so Session can handle it via new Session.close()

This commit is contained in:
Joakim Erdfelt 2017-10-03 15:30:32 -07:00
parent c0dfa1dd50
commit 6faaf3346c
7 changed files with 59 additions and 119 deletions

View File

@ -180,7 +180,7 @@ public class Parser
return (flagsInUse & 0x10) != 0; return (flagsInUse & 0x10) != 0;
} }
protected void notifyFrame(final Frame f) protected void notifyFrame(final Frame f) throws WebSocketException
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("{} Notify {}",policy.getBehavior(),getIncomingFramesHandler()); LOG.debug("{} Notify {}",policy.getBehavior(),getIncomingFramesHandler());
@ -221,25 +221,14 @@ public class Parser
} }
catch (WebSocketException e) catch (WebSocketException e)
{ {
notifyWebSocketException(e); throw e;
} }
catch (Throwable t) catch (Throwable t)
{ {
LOG.warn(t); throw new WebSocketException(t);
notifyWebSocketException(new WebSocketException(t));
} }
} }
protected void notifyWebSocketException(WebSocketException e)
{
LOG.warn(e);
if (incomingFramesHandler == null)
{
return;
}
incomingFramesHandler.incomingError(e);
}
public void parse(ByteBuffer buffer) throws WebSocketException public void parse(ByteBuffer buffer) throws WebSocketException
{ {
if (buffer.remaining() <= 0) if (buffer.remaining() <= 0)
@ -265,8 +254,6 @@ public class Parser
{ {
buffer.position(buffer.limit()); // consume remaining buffer.position(buffer.limit()); // consume remaining
reset(); reset();
// let session know
notifyWebSocketException(e);
// need to throw for proper close behavior in connection // need to throw for proper close behavior in connection
throw e; throw e;
} }
@ -274,11 +261,8 @@ public class Parser
{ {
buffer.position(buffer.limit()); // consume remaining buffer.position(buffer.limit()); // consume remaining
reset(); reset();
// let session know
WebSocketException e = new WebSocketException(t);
notifyWebSocketException(e);
// need to throw for proper close behavior in connection // need to throw for proper close behavior in connection
throw e; throw new WebSocketException(t);
} }
} }

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.websocket.common; package org.eclipse.jetty.websocket.common;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -26,6 +27,7 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
@ -39,10 +41,15 @@ import org.eclipse.jetty.websocket.common.test.UnitGenerator;
import org.eclipse.jetty.websocket.common.test.UnitParser; import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.eclipse.jetty.websocket.common.util.Hex; import org.eclipse.jetty.websocket.common.util.Hex;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
public class ParserTest public class ParserTest
{ {
@Rule
public ExpectedException expectedException = ExpectedException.none();
/** /**
* Similar to the server side 5.15 testcase. A normal 2 fragment text text message, followed by another continuation. * Similar to the server side 5.15 testcase. A normal 2 fragment text text message, followed by another continuation.
*/ */
@ -61,11 +68,9 @@ public class ParserTest
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
expectedException.expect(ProtocolException.class);
expectedException.expectMessage(containsString("CONTINUATION frame without prior !FIN"));
parser.parseQuietly(completeBuf); parser.parseQuietly(completeBuf);
capture.assertErrorCount(1);
capture.assertHasFrame(OpCode.TEXT,1);
capture.assertHasFrame(OpCode.CONTINUATION,1);
} }
/** /**
@ -83,10 +88,10 @@ public class ParserTest
UnitParser parser = new UnitParser(); UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
parser.parseQuietly(completeBuf);
capture.assertErrorCount(1); expectedException.expect(ProtocolException.class);
capture.assertHasFrame(OpCode.TEXT,1); // fragment 1 expectedException.expectMessage(containsString("Unexpected TEXT frame"));
parser.parseQuietly(completeBuf);
} }
/** /**

View File

@ -28,17 +28,21 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import org.eclipse.jetty.websocket.api.MessageTooLargeException; import org.eclipse.jetty.websocket.api.MessageTooLargeException;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
import org.eclipse.jetty.websocket.common.test.UnitParser; import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.eclipse.jetty.websocket.common.util.MaskedByteBuffer; import org.eclipse.jetty.websocket.common.util.MaskedByteBuffer;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
public class TextPayloadParserTest public class TextPayloadParserTest
{ {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test @Test
public void testFrameTooLargeDueToPolicy() throws Exception public void testFrameTooLargeDueToPolicy() throws Exception
{ {
@ -63,19 +67,15 @@ public class TextPayloadParserTest
UnitParser parser = new UnitParser(policy); UnitParser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
expectedException.expect(MessageTooLargeException.class);
parser.parseQuietly(buf); parser.parseQuietly(buf);
capture.assertHasErrors(MessageTooLargeException.class,1);
capture.assertHasNoFrames();
MessageTooLargeException err = (MessageTooLargeException)capture.getErrors().poll();
Assert.assertThat("Error.closeCode",err.getStatusCode(),is(StatusCode.MESSAGE_TOO_LARGE));
} }
@Test @Test
public void testLongMaskedText() throws Exception public void testLongMaskedText() throws Exception
{ {
StringBuffer sb = new StringBuffer(); ; StringBuffer sb = new StringBuffer();
for (int i = 0; i < 3500; i++) for (int i = 0; i < 3500; i++)
{ {
sb.append("Hell\uFF4f Big W\uFF4Frld "); sb.append("Hell\uFF4f Big W\uFF4Frld ");

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.websocket.common.ab; package org.eclipse.jetty.websocket.common.ab;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -39,11 +40,16 @@ import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
import org.eclipse.jetty.websocket.common.test.UnitGenerator; import org.eclipse.jetty.websocket.common.test.UnitGenerator;
import org.eclipse.jetty.websocket.common.test.UnitParser; import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
public class TestABCase2 public class TestABCase2
{ {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT); @Rule
public ExpectedException expectedException = ExpectedException.none();
private WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT);
@Test @Test
public void testGenerate125OctetPingCase2_4() public void testGenerate125OctetPingCase2_4()
@ -315,9 +321,10 @@ public class TestABCase2
UnitParser parser = new UnitParser(policy); UnitParser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
parser.parseQuietly(expected);
Assert.assertEquals("error should be returned for too large of ping payload",1,capture.getErrorCount(ProtocolException.class)); expectedException.expect(ProtocolException.class);
expectedException.expectMessage(containsString("Invalid control frame payload length"));
parser.parseQuietly(expected);
} }
} }

View File

@ -23,16 +23,19 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.ProtocolException; import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.Parser; import org.eclipse.jetty.websocket.common.Parser;
import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
import org.eclipse.jetty.websocket.common.test.UnitParser; import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.junit.Assert; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
public class TestABCase4 public class TestABCase4
{ {
@Rule
public ExpectedException expectedException = ExpectedException.none();
private WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT); private WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT);
@Test @Test
@ -46,25 +49,13 @@ public class TestABCase4
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
try (StacklessLogging logging = new StacklessLogging(Parser.class)) try (StacklessLogging ignore = new StacklessLogging(Parser.class))
{ {
Parser parser = new UnitParser(policy); Parser parser = new UnitParser(policy);
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
try expectedException.expect(ProtocolException.class);
{
parser.parse(expected); parser.parse(expected);
} }
catch (ProtocolException ignore)
{
// ignore
}
}
Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
Throwable known = capture.getErrors().poll();
Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 11"));
} }
@Test @Test
@ -78,25 +69,13 @@ public class TestABCase4
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
try (StacklessLogging logging = new StacklessLogging(Parser.class)) try (StacklessLogging ignore = new StacklessLogging(Parser.class))
{ {
Parser parser = new UnitParser(policy); Parser parser = new UnitParser(policy);
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
try expectedException.expect(ProtocolException.class);
{
parser.parse(expected); parser.parse(expected);
} }
catch (ProtocolException ignore)
{
// ignore
}
}
Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
Throwable known = capture.getErrors().poll();
Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 12"));
} }
@Test @Test
@ -110,25 +89,13 @@ public class TestABCase4
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
try (StacklessLogging logging = new StacklessLogging(Parser.class)) try (StacklessLogging ignore = new StacklessLogging(Parser.class))
{ {
Parser parser = new UnitParser(policy); Parser parser = new UnitParser(policy);
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
try expectedException.expect(ProtocolException.class);
{
parser.parse(expected); parser.parse(expected);
} }
catch (ProtocolException ignore)
{
// ignore
}
}
Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
Throwable known = capture.getErrors().poll();
Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 3"));
} }
@Test @Test
@ -142,24 +109,12 @@ public class TestABCase4
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
try (StacklessLogging logging = new StacklessLogging(Parser.class)) try (StacklessLogging ignore = new StacklessLogging(Parser.class))
{ {
Parser parser = new UnitParser(policy); Parser parser = new UnitParser(policy);
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
try expectedException.expect(ProtocolException.class);
{
parser.parse(expected); parser.parse(expected);
} }
catch (ProtocolException ignore)
{
// ignore
}
}
Assert.assertEquals("error on undefined opcode",1,capture.getErrorCount(WebSocketException.class));
Throwable known = capture.getErrors().poll();
Assert.assertTrue("undefined option should be in message",known.getMessage().contains("Unknown opcode: 4"));
} }
} }

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.websocket.common.ab; package org.eclipse.jetty.websocket.common.ab;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -40,11 +39,16 @@ import org.eclipse.jetty.websocket.common.test.UnitGenerator;
import org.eclipse.jetty.websocket.common.test.UnitParser; import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.eclipse.jetty.websocket.common.util.Hex; import org.eclipse.jetty.websocket.common.util.Hex;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
public class TestABCase7_3 public class TestABCase7_3
{ {
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT); @Rule
public ExpectedException expectedException = ExpectedException.none();
private WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.CLIENT);
@Test @Test
public void testCase7_3_1GenerateEmptyClose() public void testCase7_3_1GenerateEmptyClose()
@ -83,7 +87,6 @@ public class TestABCase7_3
Frame pActual = capture.getFrames().poll(); Frame pActual = capture.getFrames().poll();
Assert.assertThat("CloseFrame.payloadLength",pActual.getPayloadLength(),is(0)); Assert.assertThat("CloseFrame.payloadLength",pActual.getPayloadLength(),is(0));
} }
@ -104,13 +107,8 @@ public class TestABCase7_3
UnitParser parser = new UnitParser(policy); UnitParser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
expectedException.expect(ProtocolException.class);
parser.parseQuietly(expected); parser.parseQuietly(expected);
Assert.assertEquals("error on invalid close payload",1,capture.getErrorCount(ProtocolException.class));
ProtocolException known = (ProtocolException)capture.getErrors().poll();
Assert.assertThat("Payload.message",known.getMessage(),containsString("Invalid close frame payload length"));
} }
@Test @Test
@ -338,12 +336,7 @@ public class TestABCase7_3
UnitParser parser = new UnitParser(policy); UnitParser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture(); IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture); parser.setIncomingFramesHandler(capture);
expectedException.expect(ProtocolException.class);
parser.parseQuietly(expected); parser.parseQuietly(expected);
Assert.assertEquals("error on invalid close payload",1,capture.getErrorCount(ProtocolException.class));
ProtocolException known = (ProtocolException)capture.getErrors().poll();
Assert.assertThat("Payload.message",known.getMessage(),containsString("Invalid control frame payload length"));
} }
} }

View File

@ -58,10 +58,6 @@ public class UnitParser extends Parser
{ {
parse(buf); parse(buf);
} }
catch (Exception ignore)
{
/* ignore */
}
} }
public void parseSlowly(ByteBuffer buf, int segmentSize) public void parseSlowly(ByteBuffer buf, int segmentSize)