On 2018.12.29 13:19, Masaya Suzuki wrote: > By using and sharing a packet_reader while handling a Git pack protocol > request, the same reader option is used throughout the code. This makes > it easy to set a reader option to the request parsing code. > > Signed-off-by: Masaya Suzuki <masayasuzuki@xxxxxxxxxx> > --- > builtin/archive.c | 19 ++++++------- > builtin/receive-pack.c | 60 +++++++++++++++++++++-------------------- > fetch-pack.c | 61 +++++++++++++++++++++++------------------- > remote-curl.c | 22 ++++++++++----- > send-pack.c | 37 ++++++++++++------------- > upload-pack.c | 38 +++++++++++++------------- > 6 files changed, 129 insertions(+), 108 deletions(-) > > diff --git a/builtin/archive.c b/builtin/archive.c > index d2455237c..2fe1f05ca 100644 > --- a/builtin/archive.c > +++ b/builtin/archive.c > @@ -27,10 +27,10 @@ static int run_remote_archiver(int argc, const char **argv, > const char *remote, const char *exec, > const char *name_hint) > { > - char *buf; > int fd[2], i, rv; > struct transport *transport; > struct remote *_remote; > + struct packet_reader reader; > > _remote = remote_get(remote); > if (!_remote->url[0]) > @@ -53,18 +53,19 @@ static int run_remote_archiver(int argc, const char **argv, > packet_write_fmt(fd[1], "argument %s\n", argv[i]); > packet_flush(fd[1]); > > - buf = packet_read_line(fd[0], NULL); > - if (!buf) > + packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE); > + > + if (packet_reader_read(&reader) != PACKET_READ_NORMAL) packet_read_line() can also return NULL if the packet is zero-length, so you may want to add a "|| reader.pktlen <= 0" to the condition here (and in other places where we were checking that packet_read_line() != NULL) to make sure the behavior doesn't change. See discussion on my previous attempt[1] to refactor this in builtin/archive.c. [1]: https://public-inbox.org/git/20180912053519.31085-1-steadmon@xxxxxxxxxx/ > die(_("git archive: expected ACK/NAK, got a flush packet")); > - if (strcmp(buf, "ACK")) { > - if (starts_with(buf, "NACK ")) > - die(_("git archive: NACK %s"), buf + 5); > - if (starts_with(buf, "ERR ")) > - die(_("remote error: %s"), buf + 4); > + if (strcmp(reader.line, "ACK")) { > + if (starts_with(reader.line, "NACK ")) > + die(_("git archive: NACK %s"), reader.line + 5); > + if (starts_with(reader.line, "ERR ")) > + die(_("remote error: %s"), reader.line + 4); > die(_("git archive: protocol error")); > } > > - if (packet_read_line(fd[0], NULL)) > + if (packet_reader_read(&reader) != PACKET_READ_FLUSH) And here you'd want to add "&& reader.pktlen > 0". I'll skip pointing out further instances of this issue. > die(_("git archive: expected a flush")); > > /* Now, start reading from fd[0] and spit it out to stdout */ > diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c > index 33187bd8e..81cc07370 100644 > --- a/builtin/receive-pack.c > +++ b/builtin/receive-pack.c > @@ -1569,30 +1569,29 @@ static void queue_commands_from_cert(struct command **tail, > } > } > > -static struct command *read_head_info(struct oid_array *shallow) > +static struct command *read_head_info(struct packet_reader *reader, > + struct oid_array *shallow) > { > struct command *commands = NULL; > struct command **p = &commands; > for (;;) { > - char *line; > - int len, linelen; > + int linelen; > > - line = packet_read_line(0, &len); > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > break; > > - if (len > 8 && starts_with(line, "shallow ")) { > + if (reader->pktlen > 8 && starts_with(reader->line, "shallow ")) { > struct object_id oid; > - if (get_oid_hex(line + 8, &oid)) > + if (get_oid_hex(reader->line + 8, &oid)) > die("protocol error: expected shallow sha, got '%s'", > - line + 8); > + reader->line + 8); > oid_array_append(shallow, &oid); > continue; > } > > - linelen = strlen(line); > - if (linelen < len) { > - const char *feature_list = line + linelen + 1; > + linelen = strlen(reader->line); > + if (linelen < reader->pktlen) { > + const char *feature_list = reader->line + linelen + 1; > if (parse_feature_request(feature_list, "report-status")) > report_status = 1; > if (parse_feature_request(feature_list, "side-band-64k")) > @@ -1607,28 +1606,32 @@ static struct command *read_head_info(struct oid_array *shallow) > use_push_options = 1; > } > > - if (!strcmp(line, "push-cert")) { > + if (!strcmp(reader->line, "push-cert")) { > int true_flush = 0; > - char certbuf[1024]; > + int saved_options = reader->options; > + reader->options &= ~PACKET_READ_CHOMP_NEWLINE; > > for (;;) { > - len = packet_read(0, NULL, NULL, > - certbuf, sizeof(certbuf), 0); > - if (!len) { > + packet_reader_read(reader); > + if (reader->status == PACKET_READ_FLUSH) { Similarly, here a delim packet would previously have been caught here as well. So it might be better to just check "reader->pktlen == 0" rather than looking at the status. But I see in the next "if" you're adding a new check with a die(), so I guess you're not intending to preserve the original behavior here. > true_flush = 1; > break; > } > - if (!strcmp(certbuf, "push-cert-end\n")) > + if (reader->status != PACKET_READ_NORMAL) { > + die("protocol error: got an unexpected packet"); > + } > + if (!strcmp(reader->line, "push-cert-end\n")) > break; /* end of cert */ > - strbuf_addstr(&push_cert, certbuf); > + strbuf_addstr(&push_cert, reader->line); > } > + reader->options = saved_options; > > if (true_flush) > break; > continue; > } > > - p = queue_command(p, line, linelen); > + p = queue_command(p, reader->line, linelen); > } > > if (push_cert.len) > @@ -1637,18 +1640,14 @@ static struct command *read_head_info(struct oid_array *shallow) > return commands; > } > > -static void read_push_options(struct string_list *options) > +static void read_push_options(struct packet_reader *reader, > + struct string_list *options) > { > while (1) { > - char *line; > - int len; > - > - line = packet_read_line(0, &len); > - > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > break; > > - string_list_append(options, line); > + string_list_append(options, reader->line); > } > } > > @@ -1924,6 +1923,7 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) > struct oid_array shallow = OID_ARRAY_INIT; > struct oid_array ref = OID_ARRAY_INIT; > struct shallow_info si; > + struct packet_reader reader; > > struct option options[] = { > OPT__QUIET(&quiet, N_("quiet")), > @@ -1986,12 +1986,14 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix) > if (advertise_refs) > return 0; > > - if ((commands = read_head_info(&shallow)) != NULL) { > + packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE); > + > + if ((commands = read_head_info(&reader, &shallow)) != NULL) { > const char *unpack_status = NULL; > struct string_list push_options = STRING_LIST_INIT_DUP; > > if (use_push_options) > - read_push_options(&push_options); > + read_push_options(&reader, &push_options); > if (!check_cert_push_options(&push_options)) { > struct command *cmd; > for (cmd = commands; cmd; cmd = cmd->next) > diff --git a/fetch-pack.c b/fetch-pack.c > index 9691046e6..86790b9bb 100644 > --- a/fetch-pack.c > +++ b/fetch-pack.c > @@ -135,38 +135,42 @@ enum ack_type { > ACK_ready > }; > > -static void consume_shallow_list(struct fetch_pack_args *args, int fd) > +static void consume_shallow_list(struct fetch_pack_args *args, > + struct packet_reader *reader) > { > if (args->stateless_rpc && args->deepen) { > /* If we sent a depth we will get back "duplicate" > * shallow and unshallow commands every time there > * is a block of have lines exchanged. > */ > - char *line; > - while ((line = packet_read_line(fd, NULL))) { > - if (starts_with(line, "shallow ")) > + while (packet_reader_read(reader) == PACKET_READ_NORMAL) { > + if (starts_with(reader->line, "shallow ")) > continue; > - if (starts_with(line, "unshallow ")) > + if (starts_with(reader->line, "unshallow ")) > continue; > die(_("git fetch-pack: expected shallow list")); > } > + if (reader->status != PACKET_READ_FLUSH) > + die(_("git fetch-pack: expected a flush packet after shallow list")); Another behavior change here, as previously we didn't check for a final flush packet (unless I'm missing something). > } > } > > -static enum ack_type get_ack(int fd, struct object_id *result_oid) > +static enum ack_type get_ack(struct packet_reader *reader, > + struct object_id *result_oid) > { > int len; > - char *line = packet_read_line(fd, &len); > const char *arg; > > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > die(_("git fetch-pack: expected ACK/NAK, got a flush packet")); > - if (!strcmp(line, "NAK")) > + len = reader->pktlen; > + > + if (!strcmp(reader->line, "NAK")) > return NAK; > - if (skip_prefix(line, "ACK ", &arg)) { > + if (skip_prefix(reader->line, "ACK ", &arg)) { > if (!get_oid_hex(arg, result_oid)) { > arg += 40; > - len -= arg - line; > + len -= arg - reader->line; > if (len < 1) > return ACK; > if (strstr(arg, "continue")) > @@ -178,9 +182,9 @@ static enum ack_type get_ack(int fd, struct object_id *result_oid) > return ACK; > } > } > - if (skip_prefix(line, "ERR ", &arg)) > + if (skip_prefix(reader->line, "ERR ", &arg)) > die(_("remote error: %s"), arg); > - die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line); > + die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line); > } > > static void send_request(struct fetch_pack_args *args, > @@ -248,10 +252,14 @@ static int find_common(struct fetch_negotiator *negotiator, > int got_ready = 0; > struct strbuf req_buf = STRBUF_INIT; > size_t state_len = 0; > + struct packet_reader reader; > > if (args->stateless_rpc && multi_ack == 1) > die(_("--stateless-rpc requires multi_ack_detailed")); > > + packet_reader_init(&reader, fd[0], NULL, 0, > + PACKET_READ_CHOMP_NEWLINE); > + > if (!args->no_dependents) { > mark_tips(negotiator, args->negotiation_tips); > for_each_cached_alternate(negotiator, insert_one_alternate_object); > @@ -336,31 +344,30 @@ static int find_common(struct fetch_negotiator *negotiator, > state_len = req_buf.len; > > if (args->deepen) { > - char *line; > const char *arg; > struct object_id oid; > > send_request(args, fd[1], &req_buf); > - while ((line = packet_read_line(fd[0], NULL))) { > - if (skip_prefix(line, "shallow ", &arg)) { > + while (packet_reader_read(&reader) == PACKET_READ_NORMAL) { > + if (skip_prefix(reader.line, "shallow ", &arg)) { > if (get_oid_hex(arg, &oid)) > - die(_("invalid shallow line: %s"), line); > + die(_("invalid shallow line: %s"), reader.line); > register_shallow(the_repository, &oid); > continue; > } > - if (skip_prefix(line, "unshallow ", &arg)) { > + if (skip_prefix(reader.line, "unshallow ", &arg)) { > if (get_oid_hex(arg, &oid)) > - die(_("invalid unshallow line: %s"), line); > + die(_("invalid unshallow line: %s"), reader.line); > if (!lookup_object(the_repository, oid.hash)) > - die(_("object not found: %s"), line); > + die(_("object not found: %s"), reader.line); > /* make sure that it is parsed as shallow */ > if (!parse_object(the_repository, &oid)) > - die(_("error in object: %s"), line); > + die(_("error in object: %s"), reader.line); > if (unregister_shallow(&oid)) > - die(_("no shallow found: %s"), line); > + die(_("no shallow found: %s"), reader.line); > continue; > } > - die(_("expected shallow/unshallow, got %s"), line); > + die(_("expected shallow/unshallow, got %s"), reader.line); > } > } else if (!args->stateless_rpc) > send_request(args, fd[1], &req_buf); > @@ -397,9 +404,9 @@ static int find_common(struct fetch_negotiator *negotiator, > if (!args->stateless_rpc && count == INITIAL_FLUSH) > continue; > > - consume_shallow_list(args, fd[0]); > + consume_shallow_list(args, &reader); > do { > - ack = get_ack(fd[0], result_oid); > + ack = get_ack(&reader, result_oid); > if (ack) > print_verbose(args, _("got %s %d %s"), "ack", > ack, oid_to_hex(result_oid)); > @@ -469,9 +476,9 @@ static int find_common(struct fetch_negotiator *negotiator, > strbuf_release(&req_buf); > > if (!got_ready || !no_done) > - consume_shallow_list(args, fd[0]); > + consume_shallow_list(args, &reader); > while (flushes || multi_ack) { > - int ack = get_ack(fd[0], result_oid); > + int ack = get_ack(&reader, result_oid); > if (ack) { > print_verbose(args, _("got %s (%d) %s"), "ack", > ack, oid_to_hex(result_oid)); > diff --git a/remote-curl.c b/remote-curl.c > index 1220dffcd..bbd9ba0f3 100644 > --- a/remote-curl.c > +++ b/remote-curl.c > @@ -409,28 +409,36 @@ static struct discovery *discover_refs(const char *service, int for_push) > if (maybe_smart && > (5 <= last->len && last->buf[4] == '#') && > !strbuf_cmp(&exp, &type)) { > - char *line; > + struct packet_reader reader; > + packet_reader_init(&reader, -1, last->buf, last->len, > + PACKET_READ_CHOMP_NEWLINE); > > /* > * smart HTTP response; validate that the service > * pkt-line matches our request. > */ > - line = packet_read_line_buf(&last->buf, &last->len, NULL); > - if (!line) > + if (packet_reader_read(&reader) != PACKET_READ_NORMAL) > die("invalid server response; expected service, got flush packet"); > > strbuf_reset(&exp); > strbuf_addf(&exp, "# service=%s", service); > - if (strcmp(line, exp.buf)) > - die("invalid server response; got '%s'", line); > + if (strcmp(reader.line, exp.buf)) > + die("invalid server response; got '%s'", reader.line); > strbuf_release(&exp); > > /* The header can include additional metadata lines, up > * until a packet flush marker. Ignore these now, but > * in the future we might start to scan them. > */ > - while (packet_read_line_buf(&last->buf, &last->len, NULL)) > - ; > + for (;;) { > + packet_reader_read(&reader); > + if (reader.pktlen <= 0) { > + break; > + } > + } > + > + last->buf = reader.src_buffer; > + last->len = reader.src_len; > > last->proto_git = 1; > } else if (maybe_smart && > diff --git a/send-pack.c b/send-pack.c > index f69268677..913645046 100644 > --- a/send-pack.c > +++ b/send-pack.c > @@ -135,38 +135,36 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc > return 0; > } > > -static int receive_unpack_status(int in) > +static int receive_unpack_status(struct packet_reader *reader) > { > - const char *line = packet_read_line(in, NULL); > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > return error(_("unexpected flush packet while reading remote unpack status")); > - if (!skip_prefix(line, "unpack ", &line)) > - return error(_("unable to parse remote unpack status: %s"), line); > - if (strcmp(line, "ok")) > - return error(_("remote unpack failed: %s"), line); > + if (!skip_prefix(reader->line, "unpack ", &reader->line)) > + return error(_("unable to parse remote unpack status: %s"), reader->line); > + if (strcmp(reader->line, "ok")) > + return error(_("remote unpack failed: %s"), reader->line); > return 0; > } > > -static int receive_status(int in, struct ref *refs) > +static int receive_status(struct packet_reader *reader, struct ref *refs) > { > struct ref *hint; > int ret; > > hint = NULL; > - ret = receive_unpack_status(in); > + ret = receive_unpack_status(reader); > while (1) { > - char *refname; > + const char *refname; > char *msg; > - char *line = packet_read_line(in, NULL); > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > break; > - if (!starts_with(line, "ok ") && !starts_with(line, "ng ")) { > - error("invalid ref status from remote: %s", line); > + if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) { > + error("invalid ref status from remote: %s", reader->line); > ret = -1; > break; > } > > - refname = line + 3; > + refname = reader->line + 3; > msg = strchr(refname, ' '); > if (msg) > *msg++ = '\0'; > @@ -187,7 +185,7 @@ static int receive_status(int in, struct ref *refs) > continue; > } > > - if (line[0] == 'o' && line[1] == 'k') > + if (reader->line[0] == 'o' && reader->line[1] == 'k') > hint->status = REF_STATUS_OK; > else { > hint->status = REF_STATUS_REMOTE_REJECT; > @@ -390,6 +388,7 @@ int send_pack(struct send_pack_args *args, > int ret; > struct async demux; > const char *push_cert_nonce = NULL; > + struct packet_reader reader; > > /* Does the other end support the reporting? */ > if (server_supports("report-status")) > @@ -559,6 +558,8 @@ int send_pack(struct send_pack_args *args, > in = demux.out; > } > > + packet_reader_init(&reader, in, NULL, 0, PACKET_READ_CHOMP_NEWLINE); > + > if (need_pack_data && cmds_sent) { > if (pack_objects(out, remote_refs, extra_have, args) < 0) { > for (ref = remote_refs; ref; ref = ref->next) > @@ -573,7 +574,7 @@ int send_pack(struct send_pack_args *args, > * are failing, and just want the error() side effects. > */ > if (status_report) > - receive_unpack_status(in); > + receive_unpack_status(&reader); > > if (use_sideband) { > close(demux.out); > @@ -590,7 +591,7 @@ int send_pack(struct send_pack_args *args, > packet_flush(out); > > if (status_report && cmds_sent) > - ret = receive_status(in, remote_refs); > + ret = receive_status(&reader, remote_refs); > else > ret = 0; > if (args->stateless_rpc) > diff --git a/upload-pack.c b/upload-pack.c > index 5e81f1ff2..1638825ee 100644 > --- a/upload-pack.c > +++ b/upload-pack.c > @@ -354,7 +354,8 @@ static int ok_to_give_up(const struct object_array *have_obj, > min_generation); > } > > -static int get_common_commits(struct object_array *have_obj, > +static int get_common_commits(struct packet_reader *reader, > + struct object_array *have_obj, > struct object_array *want_obj) > { > struct object_id oid; > @@ -366,12 +367,11 @@ static int get_common_commits(struct object_array *have_obj, > save_commit_buffer = 0; > > for (;;) { > - char *line = packet_read_line(0, NULL); > const char *arg; > > reset_timeout(); > > - if (!line) { > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) { > if (multi_ack == 2 && got_common > && !got_other && ok_to_give_up(have_obj, want_obj)) { > sent_ready = 1; > @@ -390,7 +390,7 @@ static int get_common_commits(struct object_array *have_obj, > got_other = 0; > continue; > } > - if (skip_prefix(line, "have ", &arg)) { > + if (skip_prefix(reader->line, "have ", &arg)) { > switch (got_oid(arg, &oid, have_obj)) { > case -1: /* they have what we do not */ > got_other = 1; > @@ -416,7 +416,7 @@ static int get_common_commits(struct object_array *have_obj, > } > continue; > } > - if (!strcmp(line, "done")) { > + if (!strcmp(reader->line, "done")) { > if (have_obj->nr > 0) { > if (multi_ack) > packet_write_fmt(1, "ACK %s\n", last_hex); > @@ -425,7 +425,7 @@ static int get_common_commits(struct object_array *have_obj, > packet_write_fmt(1, "NAK\n"); > return -1; > } > - die("git upload-pack: expected SHA1 list, got '%s'", line); > + die("git upload-pack: expected SHA1 list, got '%s'", reader->line); > } > } > > @@ -826,7 +826,7 @@ static int process_deepen_not(const char *line, struct string_list *deepen_not, > return 0; > } > > -static void receive_needs(struct object_array *want_obj) > +static void receive_needs(struct packet_reader *reader, struct object_array *want_obj) > { > struct object_array shallows = OBJECT_ARRAY_INIT; > struct string_list deepen_not = STRING_LIST_INIT_DUP; > @@ -840,33 +840,32 @@ static void receive_needs(struct object_array *want_obj) > struct object *o; > const char *features; > struct object_id oid_buf; > - char *line = packet_read_line(0, NULL); > const char *arg; > > reset_timeout(); > - if (!line) > + if (packet_reader_read(reader) != PACKET_READ_NORMAL) > break; > > - if (process_shallow(line, &shallows)) > + if (process_shallow(reader->line, &shallows)) > continue; > - if (process_deepen(line, &depth)) > + if (process_deepen(reader->line, &depth)) > continue; > - if (process_deepen_since(line, &deepen_since, &deepen_rev_list)) > + if (process_deepen_since(reader->line, &deepen_since, &deepen_rev_list)) > continue; > - if (process_deepen_not(line, &deepen_not, &deepen_rev_list)) > + if (process_deepen_not(reader->line, &deepen_not, &deepen_rev_list)) > continue; > > - if (skip_prefix(line, "filter ", &arg)) { > + if (skip_prefix(reader->line, "filter ", &arg)) { > if (!filter_capability_requested) > die("git upload-pack: filtering capability not negotiated"); > parse_list_objects_filter(&filter_options, arg); > continue; > } > > - if (!skip_prefix(line, "want ", &arg) || > + if (!skip_prefix(reader->line, "want ", &arg) || > parse_oid_hex(arg, &oid_buf, &features)) > die("git upload-pack: protocol error, " > - "expected to get object ID, not '%s'", line); > + "expected to get object ID, not '%s'", reader->line); > > if (parse_feature_request(features, "deepen-relative")) > deepen_relative = 1; > @@ -1055,6 +1054,7 @@ void upload_pack(struct upload_pack_options *options) > { > struct string_list symref = STRING_LIST_INIT_DUP; > struct object_array want_obj = OBJECT_ARRAY_INIT; > + struct packet_reader reader; > > stateless_rpc = options->stateless_rpc; > timeout = options->timeout; > @@ -1078,10 +1078,12 @@ void upload_pack(struct upload_pack_options *options) > if (options->advertise_refs) > return; > > - receive_needs(&want_obj); > + packet_reader_init(&reader, 0, NULL, 0, PACKET_READ_CHOMP_NEWLINE); > + > + receive_needs(&reader, &want_obj); > if (want_obj.nr) { > struct object_array have_obj = OBJECT_ARRAY_INIT; > - get_common_commits(&have_obj, &want_obj); > + get_common_commits(&reader, &have_obj, &want_obj); > create_pack_file(&have_obj, &want_obj); > } > } > -- > 2.20.1.415.g653613c723-goog > In general I think this looks good. If you want this to be a strict refactor with no behavior changes, you'll want to address the comments above. Otherwise, I'd prefer if you note in the commit message where/how the behavior is changing.