diff mbox series

[v2,1/2] Use packet_reader instead of packet_read_line

Message ID 20181229211915.161686-2-masayasuzuki@google.com (mailing list archive)
State New, archived
Headers show
Series Accept error packets in any context | expand

Commit Message

Masaya Suzuki Dec. 29, 2018, 9:19 p.m. UTC
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@google.com>
---
 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(-)

Comments

Jonathan Tan Jan. 7, 2019, 7:01 p.m. UTC | #1
> 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.

I see that packet_read() and packet_read_line_buf() invocations are also
removed, so it might be better to use "Use packet_reader instead of
packet_read.*" as the commit title.

The code looks correct to me - most of the changes are removals of
packet_read_line(), replaced with a packet_reader that has
PACKET_READ_CHOMP_NEWLINE. One instance is packet_read(), for which the
corresponding reader does not have PACKET_READ_CHOMP_NEWLINE (noted
below); and another instance is packet_read_line_buf(), for which the
corresponding reader is instantiated accordingly with the buffer (also
noted below).

> -		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) {
>  					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;

Here, packet_read() is used, so we shouldn't chomp the newline, so this
is correct.

> -		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;

And here, packet_reader_init() correctly initializes the packet_reader
with the buffer, and we need to know where in the buffer to continue
after parsing the additional metadata lines and the packet flush, so we
assign the state of the reader to last->buf and last->len.

(Incidentally, with this change, there is no longer any usage of
packet_read_line_buf(), but we can remove that in a subsequent patch.)

In summary, this looks like a good change. Configuration of reader
metadata (file descriptors, input buffers, and flags) is now more
centralized, and there are fewer file descriptor constants and variables
(which all look like ints) strewn around.
Josh Steadmon Jan. 7, 2019, 10:33 p.m. UTC | #2
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@google.com>
> ---
>  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@google.com/

>  		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.
Masaya Suzuki Jan. 8, 2019, 11:27 p.m. UTC | #3
On Mon, Jan 7, 2019 at 2:33 PM Josh Steadmon <steadmon@google.com> wrote:
>
> 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@google.com>
> > ---
> >  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@google.com/

That is interesting. In Documentation/technical/protocol-common.txt,
it says "Implementations SHOULD NOT send an empty pkt-line ("0004").".
The existing code won't distinguish "0000" and "0004", while "0004" is
actually not a valid pkt-line. I'll make this patch with no behavior
change, but I think we can make that behavior change to stop accepting
0004 as 0000, and remove the pktlen checks.
diff mbox series

Patch

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)
 		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)
 		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) {
 					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"));
 	}
 }
 
-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);
 	}
 }