@@ -107,29 +107,39 @@ public override async Task Connect(MessageWriter _writer, int _timeout = 5000)
107107 _ = Task . Run ( ReceiveLoop ) ;
108108 }
109109
110- public override async Task < SendErrors > Send ( MessageWriter _writer )
110+ public override async Task < SendErrors > Send ( MessageWriter message )
111111 {
112+ // Check if the connection is ready
112113 if ( state != ConnectionState . Connected || stream == null || closeSent )
113- {
114114 return SendErrors . Disconnected ;
115- }
116115
117- InvokeBeforeSend ( _writer ) ;
118- MessageWriter frame = WebSocketFrame . CreateFrame ( _writer . Buffer . AsSpan ( 0 , _writer . Length ) , _writer . Length , WebSocketOpcode . Binary , true , _mask : true ) ;
116+ // Allow any pre-send processing
117+ InvokeBeforeSend ( message ) ;
118+
119+ // Create a WebSocket frame from the message
120+ var frame = WebSocketFrame . CreateFrame (
121+ message . Buffer . AsSpan ( 0 , message . Length ) ,
122+ message . Length ,
123+ WebSocketOpcode . Binary ,
124+ true , // fin
125+ true // mask
126+ ) ;
127+
119128 try
120129 {
130+ // Write the frame to the network stream
121131 await stream . WriteAsync ( frame . Buffer , 0 , frame . Length ) ;
122132 await stream . FlushAsync ( ) ;
123133 }
124134 catch ( Exception ex )
125135 {
126136 logger ? . WriteError ( "WebSocket send failed: " + ex . Message ) ;
127- frame . Recycle ( ) ;
128137 DisconnectInternal ( InfinityInternalErrors . ConnectionDisconnected , "Send failed" ) ;
129138 return SendErrors . Disconnected ;
130139 }
131140 finally
132141 {
142+ // Always recycle the frame to avoid memory leaks
133143 frame . Recycle ( ) ;
134144 }
135145
@@ -422,49 +432,74 @@ protected override void Dispose(bool _disposing)
422432 base . Dispose ( _disposing ) ;
423433 }
424434
425- private static async Task < string > ReadHeaders ( NetworkStream _stream )
435+ private static async Task < string > ReadHeaders ( NetworkStream stream )
426436 {
427- var sb = new StringBuilder ( ) ;
437+ var builder = new StringBuilder ( ) ;
428438 byte [ ] buffer = new byte [ 1024 ] ;
429- int matched = 0 ;
439+ int consecutiveMatch = 0 ; // Tracks \r\n\r\n sequence
440+
430441 while ( true )
431442 {
432- int read = await _stream . ReadAsync ( buffer , 0 , buffer . Length ) ;
433- if ( read <= 0 ) break ;
434- sb . Append ( Encoding . ASCII . GetString ( buffer , 0 , read ) ) ;
435- for ( int i = 0 ; i < read ; i ++ )
443+ int bytesRead = await stream . ReadAsync ( buffer , 0 , buffer . Length ) ;
444+ if ( bytesRead <= 0 ) break ; // End of stream
445+
446+ // Append the chunk to the string builder
447+ builder . Append ( Encoding . ASCII . GetString ( buffer , 0 , bytesRead ) ) ;
448+
449+ // Check for end-of-headers sequence (\r\n\r\n)
450+ for ( int i = 0 ; i < bytesRead ; i ++ )
436451 {
437452 char c = ( char ) buffer [ i ] ;
438- if ( ( matched == 0 || matched == 2 ) && c == '\r ' ) matched ++ ;
439- else if ( ( matched == 1 || matched == 3 ) && c == '\n ' ) matched ++ ;
440- else matched = 0 ;
441- if ( matched == 4 ) return sb . ToString ( ) ;
453+
454+ if ( ( consecutiveMatch == 0 || consecutiveMatch == 2 ) && c == '\r ' )
455+ consecutiveMatch ++ ;
456+ else if ( ( consecutiveMatch == 1 || consecutiveMatch == 3 ) && c == '\n ' )
457+ consecutiveMatch ++ ;
458+ else
459+ consecutiveMatch = 0 ;
460+
461+ // End of headers found
462+ if ( consecutiveMatch == 4 )
463+ return builder . ToString ( ) ;
442464 }
443465 }
444- return sb . ToString ( ) ;
466+
467+ return builder . ToString ( ) ;
445468 }
446469
447- private static Dictionary < string , string > ParseHeaders ( string raw )
470+ private static Dictionary < string , string > ParseHeaders ( string rawHeaders )
448471 {
449- var dict = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
450- var lines = raw . Split ( "\r \n " , StringSplitOptions . RemoveEmptyEntries ) ;
472+ // Use case-insensitive dictionary for HTTP headers
473+ var headers = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
474+
475+ // Split the raw header string into lines, ignoring empty lines
476+ var lines = rawHeaders . Split ( "\r \n " , StringSplitOptions . RemoveEmptyEntries ) ;
477+
478+ // Start from index 1 assuming the first line is the request/status line
451479 for ( int i = 1 ; i < lines . Length ; i ++ )
452480 {
453481 var line = lines [ i ] ;
454- int idx = line . IndexOf ( ':' ) ;
455- if ( idx <= 0 ) continue ;
456- var k = line [ ..idx ] . Trim ( ) ;
457- var v = line [ ( idx + 1 ) ..] . Trim ( ) ;
458- dict [ k ] = v ;
482+
483+ // Find the first colon, which separates key and value
484+ int colonIndex = line . IndexOf ( ':' ) ;
485+ if ( colonIndex <= 0 ) continue ; // Skip malformed lines
486+
487+ // Extract key and value, trimming whitespace
488+ var key = line [ ..colonIndex ] . Trim ( ) ;
489+ var value = line [ ( colonIndex + 1 ) ..] . Trim ( ) ;
490+
491+ headers [ key ] = value ;
459492 }
460- return dict ;
493+
494+ return headers ;
461495 }
462496
463- private static string ComputeWebSocketAccept ( string _clientKey )
497+ private static string ComputeWebSocketAccept ( string clientKey )
464498 {
465- string concat = _clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
466- byte [ ] sha1 = System . Security . Cryptography . SHA1 . HashData ( Encoding . ASCII . GetBytes ( concat ) ) ;
467- return Convert . ToBase64String ( sha1 ) ;
499+ const string WebSocketGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
500+ var concatenated = clientKey + WebSocketGuid ;
501+ var hash = SHA1 . HashData ( Encoding . ASCII . GetBytes ( concatenated ) ) ;
502+ return Convert . ToBase64String ( hash ) ;
468503 }
469504 }
470505}
0 commit comments