@@ -185,18 +185,15 @@ pub fn generate_request(mut request: Request) -> Result<(Vec<u8>, String)> {
185185 name = "Origin" ;
186186 }
187187
188- writeln ! (
189- req,
190- "{}: {}\r " ,
191- name,
192- v. to_str( ) . map_err( |err| {
193- Error :: Utf8 ( format!( "{err} for header name '{name}' with value: {v:?}" ) )
194- } ) ?
195- )
196- . unwrap ( ) ;
188+ // Write header as raw bytes to support non-ASCII values.
189+ // HTTP headers are defined as octets (RFC 7230), not UTF-8 strings.
190+ req. extend_from_slice ( name. as_bytes ( ) ) ;
191+ req. extend_from_slice ( b": " ) ;
192+ req. extend_from_slice ( v. as_bytes ( ) ) ;
193+ req. extend_from_slice ( b"\r \n " ) ;
197194 }
198195
199- writeln ! ( req, "\r " ) . unwrap ( ) ;
196+ req. extend_from_slice ( b "\r \n " ) ;
200197 trace ! ( "Request: {:?}" , String :: from_utf8_lossy( & req) ) ;
201198 Ok ( ( req, key) )
202199}
@@ -408,4 +405,53 @@ mod tests {
408405 let request = http:: Request :: builder ( ) . method ( "GET" ) . body ( ( ) ) . unwrap ( ) ;
409406 assert ! ( generate_request( request) . is_err( ) ) ;
410407 }
408+
409+ #[ test]
410+ fn request_with_non_ascii_header ( ) {
411+ use http:: header:: HeaderValue ;
412+
413+ let mut request = "ws://localhost/path" . into_client_request ( ) . unwrap ( ) ;
414+
415+ // Add a header with non-ASCII value (UTF-8 encoded "Montréal")
416+ let non_ascii_value = HeaderValue :: from_bytes ( b"Montr\xc3 \xa9 al" ) . unwrap ( ) ;
417+ request. headers_mut ( ) . insert ( "X-City" , non_ascii_value) ;
418+
419+ // This should succeed, not fail with UTF-8 error
420+ let result = generate_request ( request) ;
421+ assert ! ( result. is_ok( ) , "generate_request should accept non-ASCII header values" ) ;
422+
423+ let ( req_bytes, _key) = result. unwrap ( ) ;
424+
425+ // Verify the complete header with non-ASCII value is preserved in the output
426+ let expected_header = b"x-city: Montr\xc3 \xa9 al\r \n " ;
427+ assert ! (
428+ req_bytes. windows( expected_header. len( ) ) . any( |window| window == expected_header) ,
429+ "Request should contain the complete non-ASCII header value"
430+ ) ;
431+ }
432+
433+ #[ test]
434+ fn request_with_latin1_header ( ) {
435+ use http:: header:: HeaderValue ;
436+
437+ let mut request = "ws://localhost/path" . into_client_request ( ) . unwrap ( ) ;
438+
439+ // Add a header with ISO-8859-1 (Latin-1) encoded value
440+ // This is NOT valid UTF-8 but is valid for HTTP headers
441+ let latin1_value = HeaderValue :: from_bytes ( b"caf\xe9 " ) . unwrap ( ) ; // "café" in Latin-1
442+ request. headers_mut ( ) . insert ( "X-Test" , latin1_value) ;
443+
444+ // This should succeed
445+ let result = generate_request ( request) ;
446+ assert ! ( result. is_ok( ) , "generate_request should accept Latin-1 header values" ) ;
447+
448+ let ( req_bytes, _key) = result. unwrap ( ) ;
449+
450+ // Verify the raw bytes are preserved in the output
451+ let expected_header = b"x-test: caf\xe9 \r \n " ;
452+ assert ! (
453+ req_bytes. windows( expected_header. len( ) ) . any( |window| window == expected_header) ,
454+ "Request should preserve the raw Latin-1 bytes"
455+ ) ;
456+ }
411457}
0 commit comments