Skip to content

Commit

Permalink
Merge branch 'js/smart-http-detect-remote-error'
Browse files Browse the repository at this point in the history
Some errors from the other side coming over smart HTTP transport
were not noticed, which has been corrected.

* js/smart-http-detect-remote-error:
  t5551: test server-side ERR packet
  remote-curl: tighten "version 2" check for smart-http
  remote-curl: refactor smart-http discovery
  • Loading branch information
gitster committed Feb 9, 2019
2 parents 5a5f408 + 30dea56 commit fd357c4
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 43 deletions.
100 changes: 57 additions & 43 deletions remote-curl.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,63 @@ static int get_protocol_http_header(enum protocol_version version,
return 0;
}

static void check_smart_http(struct discovery *d, const char *service,
struct strbuf *type)
{
const char *p;
struct packet_reader reader;

/*
* If we don't see x-$service-advertisement, then it's not smart-http.
* But once we do, we commit to it and assume any other protocol
* violations are hard errors.
*/
if (!skip_prefix(type->buf, "application/x-", &p) ||
!skip_prefix(p, service, &p) ||
strcmp(p, "-advertisement"))
return;

packet_reader_init(&reader, -1, d->buf, d->len,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
die("invalid server response; expected service, got flush packet");

if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
/*
* 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.
*/
for (;;) {
packet_reader_read(&reader);
if (reader.pktlen <= 0) {
break;
}
}

/*
* v0 smart http; callers expect us to soak up the
* service and header packets
*/
d->buf = reader.src_buffer;
d->len = reader.src_len;
d->proto_git = 1;

} else if (!strcmp(reader.line, "version 2")) {
/*
* v2 smart http; do not consume version packet, which will
* be handled elsewhere.
*/
d->proto_git = 1;

} else {
die("invalid server response; got '%s'", reader.line);
}
}

static struct discovery *discover_refs(const char *service, int for_push)
{
struct strbuf exp = STRBUF_INIT;
struct strbuf type = STRBUF_INIT;
struct strbuf charset = STRBUF_INIT;
struct strbuf buffer = STRBUF_INIT;
Expand Down Expand Up @@ -405,55 +459,15 @@ static struct discovery *discover_refs(const char *service, int for_push)
last->buf_alloc = strbuf_detach(&buffer, &last->len);
last->buf = last->buf_alloc;

strbuf_addf(&exp, "application/x-%s-advertisement", service);
if (maybe_smart &&
(5 <= last->len && last->buf[4] == '#') &&
!strbuf_cmp(&exp, &type)) {
struct packet_reader reader;
packet_reader_init(&reader, -1, last->buf, last->len,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);

/*
* smart HTTP response; validate that the service
* pkt-line matches our request.
*/
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(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.
*/
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 &&
last->len > 5 && starts_with(last->buf + 4, "version 2")) {
last->proto_git = 1;
}
if (maybe_smart)
check_smart_http(last, service, &type);

if (last->proto_git)
last->refs = parse_git_refs(last, for_push);
else
last->refs = parse_info_refs(last);

strbuf_release(&refs_url);
strbuf_release(&exp);
strbuf_release(&type);
strbuf_release(&charset);
strbuf_release(&effective_url);
Expand Down
1 change: 1 addition & 0 deletions t/lib-httpd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ prepare_httpd() {
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
install_script broken-smart-http.sh
install_script error-smart-http.sh
install_script error.sh
install_script apply-one-time-sed.sh

Expand Down
4 changes: 4 additions & 0 deletions t/lib-httpd/apache.conf
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Alias /auth/dumb/ www/auth/dumb/
ScriptAliasMatch /error_git_upload_pack/(.*)/git-upload-pack error.sh/
ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
ScriptAlias /broken_smart/ broken-smart-http.sh/
ScriptAlias /error_smart/ error-smart-http.sh/
ScriptAlias /error/ error.sh/
ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1
<Directory ${GIT_EXEC_PATH}>
Expand All @@ -127,6 +128,9 @@ ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1
<Files broken-smart-http.sh>
Options ExecCGI
</Files>
<Files error-smart-http.sh>
Options ExecCGI
</Files>
<Files error.sh>
Options ExecCGI
</Files>
Expand Down
3 changes: 3 additions & 0 deletions t/lib-httpd/error-smart-http.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
echo "Content-Type: application/x-git-upload-pack-advertisement"
echo
printf "%s" "0019ERR server-side error"
5 changes: 5 additions & 0 deletions t/t5551-http-fetch-smart.sh
Original file line number Diff line number Diff line change
Expand Up @@ -429,5 +429,10 @@ test_expect_success 'GIT_TRACE_CURL_NO_DATA prevents data from being traced' '
! grep "=> Send data" err
'

test_expect_success 'server-side error detected' '
test_must_fail git clone $HTTPD_URL/error_smart/repo.git 2>actual &&
grep "server-side error" actual
'

stop_httpd
test_done

0 comments on commit fd357c4

Please sign in to comment.