@@ -7,6 +7,7 @@ use crate::{
77 Result ,
88} ;
99use bytes:: { Buf , BytesMut } ;
10+ use memchr:: { memchr, memmem} ;
1011use tokio:: {
1112 io:: { AsyncRead , AsyncWrite , AsyncWriteExt } ,
1213 sync:: Mutex ,
@@ -64,25 +65,24 @@ impl Decoder for SipCodec {
6465 return Ok ( Some ( SipCodecType :: KeepaliveResponse ) ) ;
6566 }
6667
67- if let Some ( headers_end) = src . windows ( 4 ) . position ( |w| w == b"\r \n \r \n " ) {
68+ if let Some ( headers_end) = memmem :: find ( src , b"\r \n \r \n " ) {
6869 let headers = & src[ ..headers_end + 4 ] ; // include CRLFCRLF
6970
7071 // Parse Content-Length as u32 without UTF-8 conversion
7172 let mut content_length: usize = 0 ;
7273 let mut start = 0 ;
7374 while start < headers. len ( ) {
74- // find end of line
75- let mut end = start;
76- while end < headers. len ( ) && headers[ end] != b'\n' {
77- end += 1 ;
78- }
75+ // find end of line with memchr
76+ let end = memchr ( b'\n' , & headers[ start..] )
77+ . map ( |p| start + p)
78+ . unwrap_or ( headers. len ( ) ) ;
7979
8080 let mut line = & headers[ start..end] ;
8181 if let Some ( & b'\r' ) = line. last ( ) {
8282 line = & line[ ..line. len ( ) . saturating_sub ( 1 ) ] ;
8383 }
8484
85- if let Some ( colon) = line . iter ( ) . position ( | & b| b == b ':') {
85+ if let Some ( colon) = memchr ( b ':', line ) {
8686 let header = & line[ ..colon] ;
8787 let is_cl = if header. len ( ) == CL_FULL_NAME . len ( )
8888 && header
@@ -284,3 +284,178 @@ where
284284 lock. flush ( ) . await ?;
285285 Ok ( ( ) )
286286}
287+
288+ #[ cfg( test) ]
289+ mod tests {
290+ use super :: * ;
291+ use crate :: transport:: connection:: { KEEPALIVE_REQUEST , KEEPALIVE_RESPONSE } ;
292+ use bytes:: BytesMut ;
293+ use tokio_util:: codec:: Decoder ;
294+
295+ fn make_codec ( ) -> SipCodec {
296+ SipCodec :: new ( )
297+ }
298+
299+ // Minimal valid INVITE with the given body.
300+ fn invite_bytes ( body : & str ) -> Vec < u8 > {
301+ let msg = format ! (
302+ concat!(
303+ "INVITE sip:bob@biloxi.com SIP/2.0\r \n " ,
304+ "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776\r \n " ,
305+ "To: Bob <sip:bob@biloxi.com>\r \n " ,
306+ "From: Alice <sip:alice@atlanta.com>;tag=123\r \n " ,
307+ "Call-ID: abc@test\r \n " ,
308+ "CSeq: 1 INVITE\r \n " ,
309+ "Content-Type: application/sdp\r \n " ,
310+ "Content-Length: {}\r \n " ,
311+ "\r \n " ,
312+ "{}"
313+ ) ,
314+ body. len( ) ,
315+ body
316+ ) ;
317+ msg. into_bytes ( )
318+ }
319+
320+ fn register_bytes ( ) -> Vec < u8 > {
321+ concat ! (
322+ "REGISTER sip:registrar.example.com SIP/2.0\r \n " ,
323+ "Via: SIP/2.0/UDP bob:5060;branch=z9hG4bKnashds8\r \n " ,
324+ "To: Bob <sip:bob@example.com>\r \n " ,
325+ "From: Bob <sip:bob@example.com>;tag=456\r \n " ,
326+ "Call-ID: 843817637@998sdasdh09\r \n " ,
327+ "CSeq: 1 REGISTER\r \n " ,
328+ "Content-Length: 0\r \n " ,
329+ "\r \n " ,
330+ )
331+ . as_bytes ( )
332+ . to_vec ( )
333+ }
334+
335+ // ── 基本解码 ──────────────────────────────────────────────────────────────
336+
337+ #[ test]
338+ fn decode_complete_message_no_body ( ) {
339+ let mut codec = make_codec ( ) ;
340+ let mut buf = BytesMut :: from ( register_bytes ( ) . as_slice ( ) ) ;
341+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
342+ assert ! ( matches!( result, Some ( SipCodecType :: Message ( _) ) ) ) ;
343+ assert_eq ! ( buf. len( ) , 0 , "buffer should be fully consumed" ) ;
344+ }
345+
346+ #[ test]
347+ fn decode_complete_message_with_body ( ) {
348+ let body = "v=0\r \n o=- 0 0 IN IP4 127.0.0.1\r \n " ;
349+ let mut codec = make_codec ( ) ;
350+ let mut buf = BytesMut :: from ( invite_bytes ( body) . as_slice ( ) ) ;
351+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
352+ assert ! ( matches!( result, Some ( SipCodecType :: Message ( _) ) ) ) ;
353+ assert_eq ! ( buf. len( ) , 0 ) ;
354+ }
355+
356+ // ── 分包:header 尚未完整 ─────────────────────────────────────────────────
357+
358+ #[ test]
359+ fn decode_returns_none_when_headers_incomplete ( ) {
360+ let mut codec = make_codec ( ) ;
361+ let full = register_bytes ( ) ;
362+ // Feed only the first half of the message.
363+ let half = & full[ ..full. len ( ) / 2 ] ;
364+ let mut buf = BytesMut :: from ( half) ;
365+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
366+ assert ! ( result. is_none( ) , "should wait for more data" ) ;
367+ // Buffer must be untouched.
368+ assert_eq ! ( buf. as_ref( ) , half) ;
369+ }
370+
371+ // ── 分包:body 尚未完整 ───────────────────────────────────────────────────
372+
373+ #[ test]
374+ fn decode_returns_none_when_body_incomplete ( ) {
375+ let body = "v=0\r \n o=- 0 0 IN IP4 127.0.0.1\r \n " ;
376+ let mut codec = make_codec ( ) ;
377+ let full = invite_bytes ( body) ;
378+ // Feed headers + CRLFCRLF but only half the body.
379+ let headers_end = memmem:: find ( & full, b"\r \n \r \n " ) . unwrap ( ) + 4 ;
380+ let partial_body = body. len ( ) / 2 ;
381+ let partial = & full[ ..headers_end + partial_body] ;
382+ let mut buf = BytesMut :: from ( partial) ;
383+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
384+ assert ! ( result. is_none( ) ) ;
385+ assert_eq ! ( buf. len( ) , partial. len( ) ) ;
386+ }
387+
388+ // ── 粘包:两条消息连在一起 ────────────────────────────────────────────────
389+
390+ #[ test]
391+ fn decode_two_back_to_back_messages ( ) {
392+ let mut codec = make_codec ( ) ;
393+ let mut buf = BytesMut :: new ( ) ;
394+ buf. extend_from_slice ( & register_bytes ( ) ) ;
395+ buf. extend_from_slice ( & register_bytes ( ) ) ;
396+
397+ let first = codec. decode ( & mut buf) . unwrap ( ) ;
398+ assert ! ( matches!( first, Some ( SipCodecType :: Message ( _) ) ) ) ;
399+
400+ let second = codec. decode ( & mut buf) . unwrap ( ) ;
401+ assert ! ( matches!( second, Some ( SipCodecType :: Message ( _) ) ) ) ;
402+
403+ assert_eq ! ( buf. len( ) , 0 ) ;
404+ }
405+
406+ // ── Content-Length 短格式 'l' ─────────────────────────────────────────────
407+
408+ #[ test]
409+ fn decode_short_content_length_header ( ) {
410+ let body = "hello" ;
411+ let raw = format ! (
412+ concat!(
413+ "INVITE sip:bob@example.com SIP/2.0\r \n " ,
414+ "Via: SIP/2.0/UDP pc;branch=z9hG4bK1\r \n " ,
415+ "To: <sip:bob@example.com>\r \n " ,
416+ "From: <sip:alice@example.com>;tag=1\r \n " ,
417+ "Call-ID: x@y\r \n " ,
418+ "CSeq: 1 INVITE\r \n " ,
419+ "l: {}\r \n " ,
420+ "\r \n " ,
421+ "{}"
422+ ) ,
423+ body. len( ) ,
424+ body
425+ ) ;
426+ let mut codec = make_codec ( ) ;
427+ let mut buf = BytesMut :: from ( raw. as_bytes ( ) ) ;
428+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
429+ assert ! ( matches!( result, Some ( SipCodecType :: Message ( _) ) ) ) ;
430+ }
431+
432+ // ── Keepalive 帧 ──────────────────────────────────────────────────────────
433+
434+ #[ test]
435+ fn decode_keepalive_request ( ) {
436+ let mut codec = make_codec ( ) ;
437+ let mut buf = BytesMut :: from ( KEEPALIVE_REQUEST . as_ref ( ) ) ;
438+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
439+ assert ! ( matches!( result, Some ( SipCodecType :: KeepaliveRequest ) ) ) ;
440+ assert_eq ! ( buf. len( ) , 0 ) ;
441+ }
442+
443+ #[ test]
444+ fn decode_keepalive_response ( ) {
445+ let mut codec = make_codec ( ) ;
446+ let mut buf = BytesMut :: from ( KEEPALIVE_RESPONSE . as_ref ( ) ) ;
447+ let result = codec. decode ( & mut buf) . unwrap ( ) ;
448+ assert ! ( matches!( result, Some ( SipCodecType :: KeepaliveResponse ) ) ) ;
449+ assert_eq ! ( buf. len( ) , 0 ) ;
450+ }
451+
452+ // ── 消息过大 ─────────────────────────────────────────────────────────────
453+
454+ #[ test]
455+ fn decode_rejects_oversized_buffer ( ) {
456+ let mut codec = make_codec ( ) ;
457+ let mut buf = BytesMut :: from ( vec ! [ b'X' ; MAX_SIP_MESSAGE_SIZE + 1 ] . as_slice ( ) ) ;
458+ let result = codec. decode ( & mut buf) ;
459+ assert ! ( result. is_err( ) ) ;
460+ }
461+ }
0 commit comments