tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit ef4d71923326a188a37df61edfb981e0510bf412
parent da9586a9661510c87964f855f2cb433818aa9337
Author: Valentin Gosu <valentin.gosu@gmail.com>
Date:   Sat, 22 Nov 2025 14:17:31 +0000

Bug 1999659 - fix build r=necko-reviewers,kershaw,mxinden

http3server and neqo_glue must now expect header values to be a byte array,
not a string

Differential Revision: https://phabricator.services.mozilla.com/D273461

Diffstat:
Mnetwerk/socket/neqo_glue/src/lib.rs | 4++--
Mnetwerk/test/http3server/src/main.rs | 120++++++++++++++++++++++++++++++++++++++++----------------------------------------
2 files changed, 62 insertions(+), 62 deletions(-)

diff --git a/netwerk/socket/neqo_glue/src/lib.rs b/netwerk/socket/neqo_glue/src/lib.rs @@ -1796,13 +1796,13 @@ fn convert_h3_to_h1_headers(headers: &[Header], ret_headers: &mut ThinVec<u8>) - .value(); ret_headers.extend_from_slice(b"HTTP/3 "); - ret_headers.extend_from_slice(status_val.as_bytes()); + ret_headers.extend_from_slice(status_val); ret_headers.extend_from_slice(b"\r\n"); for hdr in headers.iter().filter(|&h| h.name() != ":status") { ret_headers.extend_from_slice(&sanitize_header(Cow::from(hdr.name().as_bytes()))); ret_headers.extend_from_slice(b": "); - ret_headers.extend_from_slice(&sanitize_header(Cow::from(hdr.value().as_bytes()))); + ret_headers.extend_from_slice(&sanitize_header(Cow::from(hdr.value()))); ret_headers.extend_from_slice(b"\r\n"); } ret_headers.extend_from_slice(b"\r\n"); diff --git a/netwerk/test/http3server/src/main.rs b/netwerk/test/http3server/src/main.rs @@ -248,8 +248,8 @@ impl HttpServer for Http3TestServer { match path_hdr { Some(ph) if !ph.value().is_empty() => { let path = ph.value(); - qtrace!("Serve request {}", path); - if path == "/Response421" { + qtrace!("Serve request {:?}", ph.value_utf8().unwrap_or("<invalid utf8>")); + if path == b"/Response421" { let response_body = b"0123456789".to_vec(); stream .send_headers(&[ @@ -263,30 +263,30 @@ impl HttpServer for Http3TestServer { ]) .unwrap(); self.new_response(stream, response_body, now); - } else if path == "/RequestCancelled" { + } else if path == b"/RequestCancelled" { stream .stream_stop_sending(Error::HttpRequestCancelled.code()) .unwrap(); stream .stream_reset_send(Error::HttpRequestCancelled.code()) .unwrap(); - } else if path == "/VersionFallback" { + } else if path == b"/VersionFallback" { stream .stream_stop_sending(Error::HttpVersionFallback.code()) .unwrap(); stream .stream_reset_send(Error::HttpVersionFallback.code()) .unwrap(); - } else if path == "/EarlyResponse" { + } else if path == b"/EarlyResponse" { stream.stream_stop_sending(Error::HttpNone.code()).unwrap(); - } else if path == "/RequestRejected" { + } else if path == b"/RequestRejected" { stream .stream_stop_sending(Error::HttpRequestRejected.code()) .unwrap(); stream .stream_reset_send(Error::HttpRequestRejected.code()) .unwrap(); - } else if path == "/closeafter1000ms" { + } else if path == b"/closeafter1000ms" { let response_body = b"0123456789".to_vec(); stream .send_headers(&[ @@ -309,13 +309,13 @@ impl HttpServer for Http3TestServer { .push(stream.conn.clone()); self.new_response(stream, response_body, now); - } else if path == "/.well-known/http-opportunistic" { + } else if path == b"/.well-known/http-opportunistic" { let host_hdr = headers.iter().find(|&h| h.name() == ":authority"); match host_hdr { Some(host) if !host.value().is_empty() => { let mut content = b"[\"http://".to_vec(); - content.extend(host.value().as_bytes()); - content.extend(b"\"]".to_vec()); + content.extend(host.value()); + content.extend(b"\"]"); stream .send_headers(&[ Header::new(":status", "200"), @@ -334,7 +334,7 @@ impl HttpServer for Http3TestServer { self.new_response(stream, default_ret, now); } } - } else if path == "/no_body" { + } else if path == b"/no_body" { qdebug!("Request for no_body"); stream .send_headers(&[ @@ -343,7 +343,7 @@ impl HttpServer for Http3TestServer { ]) .unwrap(); stream.stream_close_send(now).unwrap(); - } else if path == "/no_content_length" { + } else if path == b"/no_content_length" { stream .send_headers(&[ Header::new(":status", "200"), @@ -351,7 +351,7 @@ impl HttpServer for Http3TestServer { ]) .unwrap(); self.new_response(stream, vec![b'a'; 4000], now); - } else if path == "/content_length_smaller" { + } else if path == b"/content_length_smaller" { stream .send_headers(&[ Header::new(":status", "200"), @@ -361,10 +361,10 @@ impl HttpServer for Http3TestServer { ]) .unwrap(); self.new_response(stream, vec![b'a'; 8000], now); - } else if path == "/post" { + } else if path == b"/post" { // Read all data before responding. self.posts.insert(stream, 0); - } else if path == "/priority_mirror" { + } else if path == b"/priority_mirror" { if let Some(priority) = headers.iter().find(|h| h.name() == "priority") { @@ -373,7 +373,7 @@ impl HttpServer for Http3TestServer { Header::new(":status", "200"), Header::new("cache-control", "no-cache"), Header::new("content-type", "text/plain"), - Header::new("priority-mirror", priority.value()), + Header::new("priority-mirror", priority.value_utf8().unwrap()), Header::new( "content-length", priority.value().len().to_string(), @@ -382,7 +382,7 @@ impl HttpServer for Http3TestServer { .unwrap(); self.new_response( stream, - priority.value().as_bytes().to_vec(), + priority.value().to_vec(), now, ); } else { @@ -394,11 +394,11 @@ impl HttpServer for Http3TestServer { .unwrap(); stream.stream_close_send(now).unwrap(); } - } else if path == "/103_response" { + } else if path == b"/103_response" { if let Some(early_hint) = headers.iter().find(|h| h.name() == "link-to-set") { - for l in early_hint.value().split(',') { + for l in early_hint.value_utf8().unwrap().split(',') { stream .send_headers(&[ Header::new(":status", "103"), @@ -415,7 +415,7 @@ impl HttpServer for Http3TestServer { ]) .unwrap(); stream.stream_close_send(now).unwrap(); - } else if path == "/get_webtransport_datagram" { + } else if path == b"/get_webtransport_datagram" { if let Some(dgram) = self.received_datagram.take() { stream .send_headers(&[ @@ -433,7 +433,7 @@ impl HttpServer for Http3TestServer { .unwrap(); stream.stream_close_send(now).unwrap(); } - } else if path == "/alt_svc_header" { + } else if path == b"/alt_svc_header" { if let Some(alt_svc) = headers.iter().find(|h| h.name() == "x-altsvc") { @@ -445,7 +445,7 @@ impl HttpServer for Http3TestServer { Header::new("content-length", 100.to_string()), Header::new( "alt-svc", - format!("h3={}", alt_svc.value()), + format!("h3={}", alt_svc.value_utf8().unwrap()), ), ]) .unwrap(); @@ -460,8 +460,8 @@ impl HttpServer for Http3TestServer { self.new_response(stream, vec![b'a'; 100], now); } } else { - match path.trim_matches(|p| p == '/').parse::<usize>() { - Ok(v) => { + match ph.value_utf8().ok().and_then(|s| s.trim_matches(|p| p == '/').parse::<usize>().ok()) { + Some(v) => { stream .send_headers(&[ Header::new(":status", "200"), @@ -472,7 +472,7 @@ impl HttpServer for Http3TestServer { .unwrap(); self.new_response(stream, vec![b'a'; v], now); } - Err(_) => { + None => { stream.send_headers(&default_headers).unwrap(); self.new_response(stream, default_ret, now); } @@ -555,10 +555,10 @@ impl HttpServer for Http3TestServer { match path_hdr { Some(ph) if !ph.value().is_empty() => { let path = ph.value(); - qtrace!("Serve request {}", path); - if path == "/success" { + qtrace!("Serve request {:?}", ph.value_utf8().unwrap_or("<invalid utf8>")); + if path == b"/success" { session.response(&SessionAcceptAction::Accept, now).unwrap(); - } else if path == "/redirect" { + } else if path == b"/redirect" { session .response( &SessionAcceptAction::Reject( @@ -571,7 +571,7 @@ impl HttpServer for Http3TestServer { now, ) .unwrap(); - } else if path == "/reject" { + } else if path == b"/reject" { session .response( &SessionAcceptAction::Reject( @@ -580,13 +580,13 @@ impl HttpServer for Http3TestServer { now, ) .unwrap(); - } else if path == "/closeafter0ms" { + } else if path == b"/closeafter0ms" { session.response(&SessionAcceptAction::Accept, now).unwrap(); if !self.sessions_to_close.contains_key(&now) { self.sessions_to_close.insert(now, Vec::new()); } self.sessions_to_close.get_mut(&now).unwrap().push(session); - } else if path == "/closeafter100ms" { + } else if path == b"/closeafter100ms" { session.response(&SessionAcceptAction::Accept, now).unwrap(); let expires = Instant::now() + Duration::from_millis(100); if !self.sessions_to_close.contains_key(&expires) { @@ -596,28 +596,28 @@ impl HttpServer for Http3TestServer { .get_mut(&expires) .unwrap() .push(session); - } else if path == "/create_unidi_stream" { + } else if path == b"/create_unidi_stream" { session.response(&SessionAcceptAction::Accept, now).unwrap(); self.sessions_to_create_stream.push(( session, StreamType::UniDi, None, )); - } else if path == "/create_unidi_stream_and_hello" { + } else if path == b"/create_unidi_stream_and_hello" { session.response(&SessionAcceptAction::Accept, now).unwrap(); self.sessions_to_create_stream.push(( session, StreamType::UniDi, Some(Vec::from("qwerty")), )); - } else if path == "/create_bidi_stream" { + } else if path == b"/create_bidi_stream" { session.response(&SessionAcceptAction::Accept, now).unwrap(); self.sessions_to_create_stream.push(( session, StreamType::BiDi, None, )); - } else if path == "/create_bidi_stream_and_hello" { + } else if path == b"/create_bidi_stream_and_hello" { self.webtransport_bidi_stream.clear(); session.response(&SessionAcceptAction::Accept, now).unwrap(); self.sessions_to_create_stream.push(( @@ -625,7 +625,7 @@ impl HttpServer for Http3TestServer { StreamType::BiDi, Some(Vec::from("asdfg")), )); - } else if path == "/create_bidi_stream_and_large_data" { + } else if path == b"/create_bidi_stream_and_large_data" { self.webtransport_bidi_stream.clear(); let data: Vec<u8> = vec![1u8; 32 * 1024 * 1024]; session.response(&SessionAcceptAction::Accept, now).unwrap(); @@ -857,23 +857,23 @@ impl Http3ReverseProxyServer { for hdr in request_headers.iter() { match hdr.name() { ":method" => { - *request.method_mut() = Method::from_bytes(hdr.value().as_bytes()).unwrap(); + *request.method_mut() = Method::from_bytes(hdr.value()).unwrap(); } ":scheme" => {} ":authority" => { request.headers_mut().insert( hyper::header::HOST, - HeaderValue::from_str(hdr.value()).unwrap(), + HeaderValue::from_bytes(hdr.value()).unwrap(), ); } ":path" => { - path = String::from(hdr.value()); + path = hdr.value_utf8().unwrap_or("/").to_string(); } _ => { if let Ok(hdr_name) = HeaderName::from_lowercase(hdr.name().as_bytes()) { request .headers_mut() - .insert(hdr_name, HeaderValue::from_str(hdr.value()).unwrap()); + .insert(hdr_name, HeaderValue::from_bytes(hdr.value()).unwrap()); } } } @@ -993,11 +993,11 @@ impl HttpServer for Http3ReverseProxyServer { let method_hdr = headers.iter().find(|&h| h.name() == ":method"); match method_hdr { Some(method) => match method.value() { - "POST" => { + b"POST" => { let content_length = headers.iter().find(|&h| h.name() == "content-length"); if let Some(length_str) = content_length { - if let Ok(len) = length_str.value().parse::<u32>() { + if let Ok(len) = length_str.value_utf8().unwrap_or("0").parse::<u32>() { if len > 0 { self.requests.insert(stream, (headers, Vec::new())); } else { @@ -1016,16 +1016,14 @@ impl HttpServer for Http3ReverseProxyServer { let path_hdr = headers.iter().find(|&h| h.name() == ":path"); match path_hdr { Some(ph) if !ph.value().is_empty() => { - let path = ph.value(); - match &path[..6] { - "/port?" => { - let port = path[6..].parse::<i32>(); - if let Ok(port) = port { + if let Some(path_str) = ph.value_utf8().ok() { + if let Some(port_str) = path_str.strip_prefix("/port?") { + let port = port_str.parse::<i32>().ok(); + if let Some(port) = port { qtrace!("got port {}", port); self.server_port = port; } } - _ => {} } } _ => {} @@ -1123,17 +1121,18 @@ impl HttpServer for Http3ConnectProxyServer { let method_hdr = headers.iter().find(|&h| h.name() == ":method").unwrap(); assert_eq!( method_hdr.value(), - "CONNECT", - "{} not supported", - method_hdr.value() + b"CONNECT", + "{:?} not supported", + method_hdr.value_utf8().unwrap_or("<invalid utf8>") ); let host_hdr = headers.iter().find(|&h| h.name() == ":authority").unwrap(); + let host_str = host_hdr.value_utf8().unwrap(); // Check if we should fallback to 127.0.0.1 before attempting connection - let host_without_port = if let Some(colon_pos) = host_hdr.value().rfind(':') { - &host_hdr.value()[..colon_pos] + let host_without_port = if let Some(colon_pos) = host_str.rfind(':') { + &host_str[..colon_pos] } else { - host_hdr.value() + host_str }; let should_fallback = matches!( @@ -1142,14 +1141,14 @@ impl HttpServer for Http3ConnectProxyServer { ); let target = if should_fallback { - if let Some(port_start) = host_hdr.value().rfind(':') { - format!("127.0.0.1:{}", &host_hdr.value()[port_start + 1..]) + if let Some(port_start) = host_str.rfind(':') { + format!("127.0.0.1:{}", &host_str[port_start + 1..]) } else { // No port specified, assume default HTTP port 80 "127.0.0.1:80".to_string() } } else { - host_hdr.value().to_string() + host_str.to_string() }; let tcp_stream = match std::net::TcpStream::connect(&target) { @@ -1217,18 +1216,19 @@ impl HttpServer for Http3ConnectProxyServer { session.response(&SessionAcceptAction::Accept, now).unwrap(); let host_hdr = headers.iter().find(|&h| h.name() == ":path").unwrap(); - let path_parts: Vec<&str> = host_hdr.value().split('/').collect(); + let path_str = host_hdr.value_utf8().unwrap(); + let path_parts: Vec<&str> = path_str.split('/').collect(); // Format is /.well-known/masque/udp/{target_host}/{target_port}/ if path_parts.len() < 6 { - panic!("{}", host_hdr.value()) + panic!("{}", path_str) } let target_host = path_parts[4]; let target_port = match path_parts[5].trim_end_matches('/').parse::<u16>() { Ok(port) => port, Err(_) => { - panic!("{}", host_hdr.value()) + panic!("{}", path_str) } };