Unified · Split
| @@ -152,8 +152,8 @@ impl Capabilities { · 151 unmodified lines | ||
| 152 | 152 | #[cfg(feature = "blocking-client")] |
| 153 | 153 | /// |
| 154 | 154 | pub mod recv { |
| 155 | + use bstr::ByteVec; | |
| 155 | 156 | use std::io; |
| 156 | − use std::io::Read; | |
| 157 | 157 | |
| 158 | 158 | use crate::{client, client::Capabilities, Protocol}; |
| 159 | 159 | |
| @@ -204,7 +204,18 @@ pub mod recv { · 44 unmodified lines | ||
| 204 | 204 | capabilities: { |
| 205 | 205 | let mut rd = rd.as_read(); |
| 206 | 206 | let mut buf = Vec::new(); |
| 207 | − rd.read_to_end(&mut buf)?; | |
| 207 | + while let Some(line) = rd.read_data_line() { | |
| 208 | + let line = line??; | |
| 209 | + match line.as_bstr() { | |
| 210 | + Some(line) => { | |
| 211 | + buf.push_str(line); | |
| 212 | + if buf.last() != Some(&b'\n') { | |
| 213 | + buf.push(b'\n'); | |
| 214 | + } | |
| 215 | + } | |
| 216 | + None => break, | |
| 217 | + } | |
| 218 | + } | |
| 208 | 219 | Capabilities::from_lines(buf.into())? |
| 209 | 220 | }, |
| 210 | 221 | refs: None, |
| @@ -220,9 +231,9 @@ pub mod recv { · 9 unmodified lines | ||
| 220 | 231 | /// |
| 221 | 232 | pub mod recv { |
| 222 | 233 | use futures_io::AsyncRead; |
| 223 | − use futures_lite::AsyncReadExt; | |
| 224 | 234 | |
| 225 | 235 | use crate::{client, client::Capabilities, Protocol}; |
| 236 | + use bstr::ByteVec; | |
| 226 | 237 | |
| 227 | 238 | /// Success outcome of [`Capabilities::from_lines_with_version_detection`]. |
| 228 | 239 | pub struct Outcome<'a> { |
| @@ -272,7 +283,18 @@ pub mod recv { · 43 unmodified lines | ||
| 272 | 283 | capabilities: { |
| 273 | 284 | let mut rd = rd.as_read(); |
| 274 | 285 | let mut buf = Vec::new(); |
| 275 | − rd.read_to_end(&mut buf).await?; | |
| 286 | + while let Some(line) = rd.read_data_line().await { | |
| 287 | + let line = line??; | |
| 288 | + match line.as_bstr() { | |
| 289 | + Some(line) => { | |
| 290 | + buf.push_str(line); | |
| 291 | + if buf.last() != Some(&b'\n') { | |
| 292 | + buf.push(b'\n'); | |
| 293 | + } | |
| 294 | + } | |
| 295 | + None => break, | |
| 296 | + } | |
| 297 | + } | |
| 276 | 298 | Capabilities::from_lines(buf.into())? |
| 277 | 299 | }, |
| 278 | 300 | refs: None, |
| @@ -335,6 +335,65 @@ fn handshake_and_lsrefs_and_fetch_v2() -> crate::Result { · 334 unmodified lines | ||
| 335 | 335 | handshake_and_lsrefs_and_fetch_v2_impl("v2/http-handshake.response") |
| 336 | 336 | } |
| 337 | 337 | |
| 338 | +#[test] | |
| 339 | +fn handshake_and_lsrefs_and_fetch_v2_googlesource() -> crate::Result { | |
| 340 | + let (_server, mut c) = mock::serve_and_connect( | |
| 341 | + "v2/http-no-newlines-handshake.response", | |
| 342 | + "path/not/important/due/to/mock", | |
| 343 | + Protocol::V2, | |
| 344 | + )?; | |
| 345 | + assert!( | |
| 346 | + !c.connection_persists_across_multiple_requests(), | |
| 347 | + "http connections are never stateful" | |
| 348 | + ); | |
| 349 | + let SetServiceResponse { | |
| 350 | + actual_protocol, | |
| 351 | + capabilities, | |
| 352 | + refs, | |
| 353 | + } = c.handshake(Service::UploadPack, &[("value-only", None), ("key", Some("value"))])?; | |
| 354 | + assert_eq!(actual_protocol, Protocol::V2); | |
| 355 | + assert!( | |
| 356 | + refs.is_none(), | |
| 357 | + "refs are only returned in V1, as V2 favors a separate command (with more options)" | |
| 358 | + ); | |
| 359 | + assert_eq!( | |
| 360 | + capabilities | |
| 361 | + .iter() | |
| 362 | + .map(|v| { | |
| 363 | + ( | |
| 364 | + v.name().to_owned(), | |
| 365 | + v.values().map(|v| v.map(ToOwned::to_owned).collect::<Vec<_>>()), | |
| 366 | + ) | |
| 367 | + }) | |
| 368 | + .collect::<Vec<_>>(), | |
| 369 | + [ | |
| 370 | + ("ls-refs", None), | |
| 371 | + ( | |
| 372 | + "fetch", | |
| 373 | + Some( | |
| 374 | + &[ | |
| 375 | + "filter", | |
| 376 | + "ref-in-want", | |
| 377 | + "sideband-all", | |
| 378 | + "packfile-uris", | |
| 379 | + "wait-for-done", | |
| 380 | + "shallow" | |
| 381 | + ][..] | |
| 382 | + ) | |
| 383 | + ), | |
| 384 | + ("server-option", None), | |
| 385 | + ("session-id", None), | |
| 386 | + ] | |
| 387 | + .iter() | |
| 388 | + .map(|(k, v)| ( | |
| 389 | + k.as_bytes().into(), | |
| 390 | + v.map(|v| v.iter().map(|v| v.as_bytes().into()).collect::<Vec<_>>()) | |
| 391 | + )) | |
| 392 | + .collect::<Vec<_>>() | |
| 393 | + ); | |
| 394 | + Ok(()) | |
| 395 | +} | |
| 396 | + | |
| 338 | 397 | #[test] |
| 339 | 398 | fn handshake_and_lsrefs_and_fetch_v2_service_announced() -> crate::Result { |
| 340 | 399 | handshake_and_lsrefs_and_fetch_v2_impl("v2/http-handshake-service-announced.response") |
| @@ -0,0 +1,15 @@ | ||
| 1 | +HTTP/1.1 200 OK | |
| 2 | +Cache-Control: no-cache, max-age=0, must-revalidate | |
| 3 | +Content-Length: 136 | |
| 4 | +Content-Security-Policy-Report-Only: script-src 'nonce-3J1VJWYTKuii1QtanCSwhA' 'unsafe-inline' 'strict-dynamic' https: http: 'unsafe-eval';object-src 'none';base-uri 'self';report-uri https://csp.withgoogle.com/csp/gerritcodereview/1 | |
| 5 | +Content-Type: application/x-git-upload-pack-advertisement | |
| 6 | +Expires: Fri, 01 Jan 1980 00:00:00 GMT | |
| 7 | +Pragma: no-cache | |
| 8 | +Strict-Transport-Security: max-age=31536000; includeSubDomains; preload | |
| 9 | +X-Content-Type-Options: nosniff | |
| 10 | +X-Frame-Options: SAMEORIGIN | |
| 11 | +X-Xss-Protection: 0 | |
| 12 | +Date: Sun, 04 Dec 2022 10:16:06 GMT | |
| 13 | +Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43" | |
| 14 | + | |
| 15 | +000dversion 2000bls-refs004dfetch=filter ref-in-want sideband-all packfile-uris wait-for-done shallow0011server-option000esession-id0000 | |