From da063d9a822d614c18486a7ea3ccba4c3f8a1871 Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Fri, 5 May 2023 12:49:41 +0200 Subject: [PATCH 1/2] Encoder for ACK Signed-off-by: Romain Calascibetta --- src/not-so-smart/find_common.ml | 5 +++-- src/not-so-smart/protocol.ml | 28 ++++++++++++++++++++++++++++ src/not-so-smart/protocol.mli | 3 +++ src/not-so-smart/smart.ml | 5 ++++- src/not-so-smart/smart.mli | 3 ++- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/not-so-smart/find_common.ml b/src/not-so-smart/find_common.ml index ece94e862..8518186a2 100644 --- a/src/not-so-smart/find_common.ml +++ b/src/not-so-smart/find_common.ml @@ -172,7 +172,8 @@ let find_common (type t) scheduler io flow cfg consume_shallow_list scheduler io flow cfg None hex ctx >>= fun _shallows -> let rec loop () = - Smart_flow.run scheduler raise io flow Smart.(recv ctx ack) + Smart_flow.run scheduler raise io flow + Smart.(recv ctx recv_ack) >>| Smart.Negotiation.map ~f:of_hex >>= fun ack -> match ack with @@ -253,7 +254,7 @@ let find_common (type t) scheduler io flow cfg let rec go () = if !flushes > 0 || cfg.multi_ack = `Some || cfg.multi_ack = `Detailed then ( - Smart_flow.run scheduler raise io flow Smart.(recv ctx ack) + Smart_flow.run scheduler raise io flow Smart.(recv ctx recv_ack) >>| Smart.Negotiation.map ~f:of_hex >>= fun ack -> match ack with diff --git a/src/not-so-smart/protocol.ml b/src/not-so-smart/protocol.ml index 3d0686d91..95ddf5f2b 100644 --- a/src/not-so-smart/protocol.ml +++ b/src/not-so-smart/protocol.ml @@ -1227,4 +1227,32 @@ module Encoder = struct flush (go buffer (off + len) (max - len)) encoder in go payload 0 (String.length payload) encoder + + let encode_acks encoder acks = + (* TODO: Remove NACK from [Negotiation.t]. *) + let write_ack ack encoder = + let write_ack uid suffix = + write encoder "ACK"; + write_space encoder; + write encoder uid; + match suffix with + | None -> () + | Some s -> + write_space encoder; + write encoder s + in + match ack with + | Negotiation.ACK uid -> write_ack uid None + | ACK_continue uid -> write_ack uid (Some "continue") + | ACK_ready uid -> write_ack uid (Some "ready") + | ACK_common uid -> write_ack uid (Some "common") + | NAK -> write encoder "NAK" + in + let rec go acks encoder = + match acks with + | [] -> + delayed_write_pkt (write_ack Negotiation.NAK) (flush kdone) encoder + | hd :: tl -> delayed_write_pkt (write_ack hd) (go tl) encoder + in + go acks encoder end diff --git a/src/not-so-smart/protocol.mli b/src/not-so-smart/protocol.mli index 5b1c0ce07..0ab991b34 100644 --- a/src/not-so-smart/protocol.mli +++ b/src/not-so-smart/protocol.mli @@ -224,6 +224,9 @@ module Encoder : sig val encode_flush : encoder -> error state val encode_commands : encoder -> (string, string) Commands.t -> error state + val encode_acks : encoder -> string Negotiation.t list -> error state + (** Sends a list of [ACK]s and terminate with a [NAK]. *) + val encode_advertised_refs : encoder -> (string, string) Advertised_refs.t -> error state diff --git a/src/not-so-smart/smart.ml b/src/not-so-smart/smart.ml index c4657e52b..b6c6dbcf8 100644 --- a/src/not-so-smart/smart.ml +++ b/src/not-so-smart/smart.ml @@ -24,6 +24,7 @@ module Witness = struct | Commands : (string, string) Commands.t send | Send_pack : { side_band : bool; stateless : bool } -> string send | Advertised_refs : (string, string) Advertised_refs.t send + | Acks : string Negotiation.t list send type 'a recv = | Advertised_refs : (string, string) Advertised_refs.t recv @@ -72,6 +73,7 @@ module Value = struct encode_pack ~side_band ~stateless encoder v | Flush -> encode_flush encoder | Advertised_refs -> encode_advertised_refs encoder v + | Acks -> encode_acks encoder v in let rec translate_to_state_t = function | Encoder.Done -> State.Return () @@ -152,7 +154,8 @@ let recv_pack ?(push_stdout = ignore) ?(push_stderr = ignore) side_band = let recv_flush : _ recv = Flush let status sideband = Status sideband let flush : _ send = Flush -let ack = Ack +let recv_ack : _ recv = Ack +let send_acks : _ send = Acks let shallows = Shallows let send_pack ?(stateless = false) side_band = diff --git a/src/not-so-smart/smart.mli b/src/not-so-smart/smart.mli index 8cff56d10..a60e2d4c5 100644 --- a/src/not-so-smart/smart.mli +++ b/src/not-so-smart/smart.mli @@ -244,7 +244,8 @@ val recv_pack : val recv_flush : unit recv val recv_commands : (string, string) Commands.t option recv -val ack : string Negotiation.t recv +val send_acks : string Negotiation.t list send +val recv_ack : string Negotiation.t recv val shallows : string Shallow.t list recv val status : bool -> string Status.t recv val packet : trim:bool -> string recv From 12d4e1588ebe055b17ee37903f08c220117ffe32 Mon Sep 17 00:00:00 2001 From: Paul-Elliot Date: Sat, 6 May 2023 12:29:01 +0200 Subject: [PATCH 2/2] upload-pack: Handle empty request The client uses empty requests when: - It is already up to date - `git ls-remote` Signed-off-by: Paul-Elliot --- fuzz/smart.ml | 10 ++++++---- src/not-so-smart/protocol.ml | 7 +++++-- src/not-so-smart/protocol.mli | 5 ++++- src/not-so-smart/smart.ml | 2 +- src/not-so-smart/smart.mli | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/fuzz/smart.ml b/fuzz/smart.ml index 379fe9cdb..a94b956ae 100644 --- a/fuzz/smart.ml +++ b/fuzz/smart.ml @@ -193,7 +193,9 @@ let () = let wants = List.map Digestif.SHA1.to_hex wants in let v = Smart.Want.v ~capabilities:[] wants in let str = to_string v in - let res = of_string str in - Crowbar.check_eq ~pp:Smart.Want.pp - ~eq:(Smart.Want.equal ~uid:String.equal ~reference:String.equal) - v res + match of_string str with + | Some res -> + Crowbar.check_eq ~pp:Smart.Want.pp + ~eq:(Smart.Want.equal ~uid:String.equal ~reference:String.equal) + v res + | None -> Crowbar.failf "Invalid empty fetch request (%S)" str diff --git a/src/not-so-smart/protocol.ml b/src/not-so-smart/protocol.ml index 95ddf5f2b..ba20f3672 100644 --- a/src/not-so-smart/protocol.ml +++ b/src/not-so-smart/protocol.ml @@ -623,7 +623,9 @@ module Decoder = struct let v = peek_pkt decoder in if Sub.is_empty v then ( junk_pkt decoder; - return (Want.v ~capabilities (first_want :: List.rev wants)) decoder + return + (Some (Want.v ~capabilities (first_want :: List.rev wants))) + decoder (* TODO else if start with shallow or depth request or filter request then *)) else match Sub.cut ~sep:v_space v with @@ -639,7 +641,8 @@ module Decoder = struct let decode_first_want decoder = let v = peek_pkt decoder in - if Sub.is_prefix v ~affix:v_want then ( + if Sub.is_empty v then return None decoder + else if Sub.is_prefix v ~affix:v_want then ( let v = v |> Sub.with_range ~first:(Sub.length v_want) in (* NOTE(dinosaure): we accept more than Git. The BNF syntax of [first-want] is: diff --git a/src/not-so-smart/protocol.mli b/src/not-so-smart/protocol.mli index 0ab991b34..35cefa818 100644 --- a/src/not-so-smart/protocol.mli +++ b/src/not-so-smart/protocol.mli @@ -205,7 +205,10 @@ module Decoder : sig ?sideband:bool -> decoder -> (string Status.t, [> error ]) state val decode_packet : trim:bool -> decoder -> (string, [> error ]) state - val decode_want : decoder -> ((string, string) Want.t, [> error ]) state + + val decode_want : + decoder -> ((string, string) Want.t option, [> error ]) state + val decode_have : decoder -> (string Have.t, [> error ]) state val decode_commands : diff --git a/src/not-so-smart/smart.ml b/src/not-so-smart/smart.ml index b6c6dbcf8..9183adb7b 100644 --- a/src/not-so-smart/smart.ml +++ b/src/not-so-smart/smart.ml @@ -45,7 +45,7 @@ module Witness = struct | Ack : string Negotiation.t recv | Flush : unit recv | Shallows : string Shallow.t list recv - | Want : (string, string) Want.t recv + | Want : (string, string) Want.t option recv | Have : string Have.t recv end diff --git a/src/not-so-smart/smart.mli b/src/not-so-smart/smart.mli index a60e2d4c5..fc92e865a 100644 --- a/src/not-so-smart/smart.mli +++ b/src/not-so-smart/smart.mli @@ -250,7 +250,7 @@ val shallows : string Shallow.t list recv val status : bool -> string Status.t recv val packet : trim:bool -> string recv val send_advertised_refs : (string, string) Advertised_refs.t send -val recv_want : (string, string) Want.t recv +val recv_want : (string, string) Want.t option recv val recv_have : string Have.t recv val bind : ('a, 'err) t -> f:('a -> ('b, 'err) t) -> ('b, 'err) t val ( let* ) : ('a, 'err) t -> ('a -> ('b, 'err) t) -> ('b, 'err) t