@@ -1003,7 +1003,6 @@ mod fragmentation {
10031003}
10041004
10051005mod auto {
1006-
10071006 use crate :: {
10081007 CloseFrame ,
10091008 error:: { Error , WriteError } ,
@@ -1107,9 +1106,6 @@ mod auto {
11071106 assert ! ( matches!( error, Error :: Write ( WriteError :: ConnectionClosed ) ) ) ;
11081107 }
11091108 }
1110-
1111- // Reading after eof should always return None
1112- assert ! ( next!( websocketz) . is_none( ) ) ;
11131109 } ;
11141110
11151111 let server = async move {
@@ -1140,4 +1136,122 @@ mod auto {
11401136 }
11411137}
11421138
1143- // TODO: test for read errors
1139+ mod protocol {
1140+ use tokio:: io:: AsyncWriteExt ;
1141+
1142+ use crate :: error:: { Error , ProtocolError , ReadError } ;
1143+
1144+ use super :: * ;
1145+
1146+ macro_rules! quick_protocol_error {
1147+ ( $frame: ident, $error: ident) => {
1148+ let ( client, mut server) = tokio:: io:: duplex( 16 ) ;
1149+
1150+ let client = async move {
1151+ let read_buf = & mut [ 0u8 ; SIZE ] ;
1152+ let write_buf = & mut [ 0u8 ; SIZE ] ;
1153+ let fragments_buf = & mut [ 0u8 ; SIZE ] ;
1154+
1155+ let mut websocketz = WebSocket :: client(
1156+ FromTokio :: new( client) ,
1157+ StdRng :: from_os_rng( ) ,
1158+ read_buf,
1159+ write_buf,
1160+ fragments_buf,
1161+ ) ;
1162+
1163+ match next!( websocketz) {
1164+ Some ( Err ( error) ) => {
1165+ std:: println!( "Received error: {error:?}" ) ;
1166+ assert!( matches!(
1167+ error,
1168+ Error :: Read ( ReadError :: Protocol ( ProtocolError :: $error) )
1169+ ) ) ;
1170+ }
1171+ message => panic!( "Unexpected message: {message:?}" ) ,
1172+ }
1173+ } ;
1174+
1175+ let server = async move {
1176+ server. write_all( $frame) . await . unwrap( ) ;
1177+
1178+ server
1179+ } ;
1180+
1181+ tokio:: join!( client, server) ;
1182+ } ;
1183+ }
1184+
1185+ #[ tokio:: test]
1186+ async fn invalid_close_frame ( ) {
1187+ const FRAME : & [ u8 ] = & [
1188+ 0x88 , // FIN=1, RSV1-3=0, opcode=0x8 (Close)
1189+ 0x01 , // MASK=0 (unmasked), payload length = 1
1190+ 0x37 , // Single byte of payload (invalid)
1191+ ] ;
1192+
1193+ quick_protocol_error ! ( FRAME , InvalidCloseFrame ) ;
1194+ }
1195+
1196+ #[ tokio:: test]
1197+ async fn invalid_close_code ( ) {
1198+ const FRAME : & [ u8 ] = & [
1199+ 0x88 , // FIN + opcode=0x8 (Close)
1200+ 0x02 , // Payload length = 2 (only status code, no reason)
1201+ 0x03 , 0xED , // Status code: 1005 (not allowed)
1202+ ] ;
1203+
1204+ quick_protocol_error ! ( FRAME , InvalidCloseCode ) ;
1205+ }
1206+
1207+ #[ tokio:: test]
1208+ async fn invalid_utf8_close ( ) {
1209+ const FRAME : & [ u8 ] = & [
1210+ 0x88 , // FIN + opcode=0x8 (Close)
1211+ 0x03 , // Payload length = 3 (2 bytes code + 1 byte invalid UTF-8)
1212+ 0x03 , 0xE8 , // Status code: 1000 (normal closure)
1213+ 0xFF , // Invalid UTF-8 byte
1214+ ] ;
1215+
1216+ quick_protocol_error ! ( FRAME , InvalidUTF8 ) ;
1217+ }
1218+
1219+ #[ tokio:: test]
1220+ async fn invalid_utf8_text ( ) {
1221+ const FRAME : & [ u8 ] = & [
1222+ 0x81 , // FIN + opcode 0x1 (text)
1223+ 0x01 , // payload length = 1
1224+ 0xFF , // invalid UTF-8 byte
1225+ ] ;
1226+
1227+ quick_protocol_error ! ( FRAME , InvalidUTF8 ) ;
1228+ }
1229+
1230+ #[ tokio:: test]
1231+ async fn invalid_fragment ( ) {
1232+ const FRAMES : & [ u8 ] = & [
1233+ // Start a fragmented text frame
1234+ 0x01 , // FIN = 0, opcode = 0x1 (Text, not final)
1235+ 0x01 , // Payload length = 1
1236+ 0x41 , // 'A'
1237+ // Try to send a new Binary message while the previous fragment isn't finished
1238+ 0x82 , // FIN = 1, opcode = 0x2 (Binary, complete)
1239+ 0x01 , // Payload length = 1
1240+ 0x42 , // 'B'
1241+ ] ;
1242+
1243+ quick_protocol_error ! ( FRAMES , InvalidFragment ) ;
1244+ }
1245+
1246+ #[ tokio:: test]
1247+ async fn invalid_continuation_frame ( ) {
1248+ // Continuation frame without a preceding fragmented message
1249+ const FRAME : & [ u8 ] = & [
1250+ 0x80 , // FIN = 1, opcode = 0x0 (Continuation)
1251+ 0x01 , // Payload length = 1
1252+ 0x41 , // ASCII 'A'
1253+ ] ;
1254+
1255+ quick_protocol_error ! ( FRAME , InvalidContinuationFrame ) ;
1256+ }
1257+ }
0 commit comments