History

fix: make it possible to parse handshakes without newlines in packetlines #(639)

Sebastian Thiel committed 3 years ago

Commit 4927adf1 parent 7ab7c243 files · +100 −4Browse files

Unified · Split

3 files
  1. git-transport/src/client/capabilities.rs +26 −4
  2. git-transport/tests/client/blocking_io/http/mod.rs +59 −0
  3. git-transport/tests/fixtures/v2/http-no-newlines-handshake.response +15 −0
git-transport/src/client/capabilities.rs+26−4
@@ -152,8 +152,8 @@ impl Capabilities { · 151 unmodified lines
152152 #[cfg(feature = "blocking-client")]
153153 ///
154154 pub mod recv {
155+ use bstr::ByteVec;
155156 use std::io;
156− use std::io::Read;
157157
158158 use crate::{client, client::Capabilities, Protocol};
159159
@@ -204,7 +204,18 @@ pub mod recv { · 44 unmodified lines
204204 capabilities: {
205205 let mut rd = rd.as_read();
206206 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+ }
208219 Capabilities::from_lines(buf.into())?
209220 },
210221 refs: None,
@@ -220,9 +231,9 @@ pub mod recv { · 9 unmodified lines
220231 ///
221232 pub mod recv {
222233 use futures_io::AsyncRead;
223− use futures_lite::AsyncReadExt;
224234
225235 use crate::{client, client::Capabilities, Protocol};
236+ use bstr::ByteVec;
226237
227238 /// Success outcome of [`Capabilities::from_lines_with_version_detection`].
228239 pub struct Outcome<'a> {
@@ -272,7 +283,18 @@ pub mod recv { · 43 unmodified lines
272283 capabilities: {
273284 let mut rd = rd.as_read();
274285 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+ }
276298 Capabilities::from_lines(buf.into())?
277299 },
278300 refs: None,
git-transport/tests/client/blocking_io/http/mod.rs+59−0
@@ -335,6 +335,65 @@ fn handshake_and_lsrefs_and_fetch_v2() -> crate::Result { · 334 unmodified lines
335335 handshake_and_lsrefs_and_fetch_v2_impl("v2/http-handshake.response")
336336 }
337337
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+
338397 #[test]
339398 fn handshake_and_lsrefs_and_fetch_v2_service_announced() -> crate::Result {
340399 handshake_and_lsrefs_and_fetch_v2_impl("v2/http-handshake-service-announced.response")
git-transport/tests/fixtures/v2/http-no-newlines-handshake.response+15−0
@@ -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