Merge branch 'js/trace2-session-id'

The transport layer was taught to optionally exchange the session
ID assigned by the trace2 subsystem during fetch/push transactions.

* js/trace2-session-id:
  receive-pack: log received client session ID
  send-pack: advertise session ID in capabilities
  upload-pack, serve: log received client session ID
  fetch-pack: advertise session ID in capabilities
  transport: log received server session ID
  serve: advertise session ID in v2 capabilities
  receive-pack: advertise session ID in v0 capabilities
  upload-pack: advertise session ID in v0 capabilities
  trace2: add a public function for getting the SID
  docs: new transfer.advertiseSID option
  docs: new capability to advertise session IDs
This commit is contained in:
Junio C Hamano 2020-12-08 15:11:20 -08:00
commit 01b8886a62
12 changed files with 198 additions and 3 deletions

View File

@ -69,3 +69,7 @@ transfer.unpackLimit::
When `fetch.unpackLimit` or `receive.unpackLimit` are
not set, the value of this variable is used instead.
The default value is 100.
transfer.advertiseSID::
Boolean. When true, client and server processes will advertise their
unique session IDs to their remote counterpart. Defaults to false.

View File

@ -27,8 +27,8 @@ and 'push-cert' capabilities are sent and recognized by the receive-pack
(push to server) process.
The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
by both upload-pack and receive-pack protocols. The 'agent' capability
may optionally be sent in both protocols.
by both upload-pack and receive-pack protocols. The 'agent' and 'session-id'
capabilities may optionally be sent in both protocols.
All other capabilities are only recognized by the upload-pack (fetch
from server) process.
@ -365,3 +365,16 @@ If the upload-pack server advertises the 'filter' capability,
fetch-pack may send "filter" commands to request a partial clone
or partial fetch and request that the server omit various objects
from the packfile.
session-id=<session id>
-----------------------
The server may advertise a session ID that can be used to identify this process
across multiple requests. The client may advertise its own session ID back to
the server as well.
Session IDs should be unique to a given process. They must fit within a
packet-line, and must not contain non-printable or whitespace characters. The
current implementation uses trace2 session IDs (see
link:api-trace2.html[api-trace2] for details), but this may change and users of
the session ID should not rely on this fact.

View File

@ -492,3 +492,16 @@ form `object-format=X`) to notify the client that the server is able to deal
with objects using hash algorithm X. If not specified, the server is assumed to
only handle SHA-1. If the client would like to use a hash algorithm other than
SHA-1, it should specify its object-format string.
session-id=<session id>
~~~~~~~~~~~~~~~~~~~~~~~
The server may advertise a session ID that can be used to identify this process
across multiple requests. The client may advertise its own session ID back to
the server as well.
Session IDs should be unique to a given process. They must fit within a
packet-line, and must not contain non-printable or whitespace characters. The
current implementation uses trace2 session IDs (see
link:api-trace2.html[api-trace2] for details), but this may change and users of
the session ID should not rely on this fact.

View File

@ -54,6 +54,7 @@ static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int advertise_atomic_push = 1;
static int advertise_push_options;
static int advertise_sid;
static int unpack_limit = 100;
static off_t max_input_size;
static int report_status;
@ -248,6 +249,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
return 0;
}
if (strcmp(var, "transfer.advertisesid") == 0) {
advertise_sid = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value, cb);
}
@ -268,6 +274,8 @@ static void show_ref(const char *path, const struct object_id *oid)
strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
if (advertise_push_options)
strbuf_addstr(&cap, " push-options");
if (advertise_sid)
strbuf_addf(&cap, " session-id=%s", trace2_session_id());
strbuf_addf(&cap, " object-format=%s", the_hash_algo->name);
strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
packet_write_fmt(1, "%s %s%c%s\n",
@ -2075,6 +2083,7 @@ static struct command *read_head_info(struct packet_reader *reader,
if (linelen < reader->pktlen) {
const char *feature_list = reader->line + linelen + 1;
const char *hash = NULL;
const char *client_sid;
int len = 0;
if (parse_feature_request(feature_list, "report-status"))
report_status = 1;
@ -2097,6 +2106,12 @@ static struct command *read_head_info(struct packet_reader *reader,
}
if (xstrncmpz(the_hash_algo->name, hash, len))
die("error: unsupported object format '%s'", hash);
client_sid = parse_feature_value(feature_list, "session-id", &len, NULL);
if (client_sid) {
char *sid = xstrndup(client_sid, len);
trace2_data_string("transfer", NULL, "client-sid", client_sid);
free(sid);
}
}
if (!strcmp(reader->line, "push-cert")) {

View File

@ -35,6 +35,7 @@ static int fetch_fsck_objects = -1;
static int transfer_fsck_objects = -1;
static int agent_supported;
static int server_supports_filtering;
static int advertise_sid;
static struct shallow_lock shallow_lock;
static const char *alternate_shallow_file;
static struct strbuf fsck_msg_types = STRBUF_INIT;
@ -326,6 +327,8 @@ static int find_common(struct fetch_negotiator *negotiator,
if (deepen_not_ok) strbuf_addstr(&c, " deepen-not");
if (agent_supported) strbuf_addf(&c, " agent=%s",
git_user_agent_sanitized());
if (advertise_sid)
strbuf_addf(&c, " session-id=%s", trace2_session_id());
if (args->filter_options.choice)
strbuf_addstr(&c, " filter");
packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf);
@ -979,6 +982,9 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
agent_len, agent_feature);
}
if (!server_supports("session-id"))
advertise_sid = 0;
if (server_supports("shallow"))
print_verbose(args, _("Server supports %s"), "shallow");
else if (args->depth > 0 || is_repository_shallow(r))
@ -1191,6 +1197,8 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
packet_buf_write(&req_buf, "command=fetch");
if (server_supports_v2("agent", 0))
packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized());
if (advertise_sid && server_supports_v2("session-id", 0))
packet_buf_write(&req_buf, "session-id=%s", trace2_session_id());
if (args->server_options && args->server_options->nr &&
server_supports_v2("server-option", 1)) {
int i;
@ -1711,6 +1719,7 @@ static void fetch_pack_config(void)
git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
git_config_get_bool("transfer.advertisesid", &advertise_sid);
if (!uri_protocols.nr) {
char *str;

View File

@ -425,6 +425,7 @@ int send_pack(struct send_pack_args *args,
int use_sideband = 0;
int quiet_supported = 0;
int agent_supported = 0;
int advertise_sid = 0;
int use_atomic = 0;
int atomic_supported = 0;
int use_push_options = 0;
@ -436,6 +437,8 @@ int send_pack(struct send_pack_args *args,
const char *push_cert_nonce = NULL;
struct packet_reader reader;
git_config_get_bool("transfer.advertisesid", &advertise_sid);
/* Does the other end support the reporting? */
if (server_supports("report-status-v2"))
status_report = 2;
@ -451,6 +454,8 @@ int send_pack(struct send_pack_args *args,
quiet_supported = 1;
if (server_supports("agent"))
agent_supported = 1;
if (!server_supports("session-id"))
advertise_sid = 0;
if (server_supports("no-thin"))
args->use_thin_pack = 0;
if (server_supports("atomic"))
@ -507,6 +512,8 @@ int send_pack(struct send_pack_args *args,
strbuf_addf(&cap_buf, " object-format=%s", the_hash_algo->name);
if (agent_supported)
strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
if (advertise_sid)
strbuf_addf(&cap_buf, " session-id=%s", trace2_session_id());
/*
* NEEDSWORK: why does delete-refs have to be so specific to

18
serve.c
View File

@ -8,6 +8,8 @@
#include "serve.h"
#include "upload-pack.h"
static int advertise_sid;
static int always_advertise(struct repository *r,
struct strbuf *value)
{
@ -30,6 +32,15 @@ static int object_format_advertise(struct repository *r,
return 1;
}
static int session_id_advertise(struct repository *r, struct strbuf *value)
{
if (!advertise_sid)
return 0;
if (value)
strbuf_addstr(value, trace2_session_id());
return 1;
}
struct protocol_capability {
/*
* The name of the capability. The server uses this name when
@ -66,6 +77,7 @@ static struct protocol_capability capabilities[] = {
{ "fetch", upload_pack_advertise, upload_pack_v2 },
{ "server-option", always_advertise, NULL },
{ "object-format", object_format_advertise, NULL },
{ "session-id", session_id_advertise, NULL },
};
static void advertise_capabilities(void)
@ -189,6 +201,7 @@ static int process_request(void)
struct packet_reader reader;
struct strvec keys = STRVEC_INIT;
struct protocol_capability *command = NULL;
const char *client_sid;
packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
@ -252,6 +265,9 @@ static int process_request(void)
check_algorithm(the_repository, &keys);
if (has_capability(&keys, "session-id", &client_sid))
trace2_data_string("transfer", NULL, "client-sid", client_sid);
command->command(the_repository, &keys, &reader);
strvec_clear(&keys);
@ -261,6 +277,8 @@ static int process_request(void)
/* Main serve loop for protocol version 2 */
void serve(struct serve_options *options)
{
git_config_get_bool("transfer.advertisesid", &advertise_sid);
if (options->advertise_capabilities || !options->stateless_rpc) {
/* serve by default supports v2 */
packet_write_fmt(1, "version 2\n");

View File

@ -0,0 +1,78 @@
#!/bin/sh
test_description='session ID in capabilities'
. ./test-lib.sh
REPO="$(pwd)/repo"
LOCAL_PRISTINE="$(pwd)/local_pristine"
test_expect_success 'setup repos for session ID capability tests' '
git init "$REPO" &&
test_commit -C "$REPO" a &&
git clone "file://$REPO" "$LOCAL_PRISTINE" &&
test_commit -C "$REPO" b
'
for PROTO in 0 1 2
do
test_expect_success "session IDs not advertised by default (fetch v${PROTO})" '
test_when_finished "rm -rf local tr2-client-events tr2-server-events" &&
cp -r "$LOCAL_PRISTINE" local &&
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local fetch \
--upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \
origin &&
test -z "$(grep \"key\":\"server-sid\" tr2-client-events)" &&
test -z "$(grep \"key\":\"client-sid\" tr2-server-events)"
'
test_expect_success "session IDs not advertised by default (push v${PROTO})" '
test_when_finished "rm -rf local tr2-client-events tr2-server-events" &&
test_when_finished "git -C local push --delete origin new-branch" &&
cp -r "$LOCAL_PRISTINE" local &&
git -C local pull --no-rebase origin &&
GIT_TRACE2_EVENT_NESTING=5 \
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local push \
--receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
origin HEAD:new-branch &&
test -z "$(grep \"key\":\"server-sid\" tr2-client-events)" &&
test -z "$(grep \"key\":\"client-sid\" tr2-server-events)"
'
done
test_expect_success 'enable SID advertisement' '
git -C "$REPO" config transfer.advertiseSID true &&
git -C "$LOCAL_PRISTINE" config transfer.advertiseSID true
'
for PROTO in 0 1 2
do
test_expect_success "session IDs advertised (fetch v${PROTO})" '
test_when_finished "rm -rf local tr2-client-events tr2-server-events" &&
cp -r "$LOCAL_PRISTINE" local &&
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local fetch \
--upload-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-upload-pack" \
origin &&
grep \"key\":\"server-sid\" tr2-client-events &&
grep \"key\":\"client-sid\" tr2-server-events
'
test_expect_success "session IDs advertised (push v${PROTO})" '
test_when_finished "rm -rf local tr2-client-events tr2-server-events" &&
test_when_finished "git -C local push --delete origin new-branch" &&
cp -r "$LOCAL_PRISTINE" local &&
git -C local pull --no-rebase origin &&
GIT_TRACE2_EVENT_NESTING=5 \
GIT_TRACE2_EVENT="$(pwd)/tr2-client-events" \
git -c protocol.version=$PROTO -C local push \
--receive-pack "GIT_TRACE2_EVENT=\"$(pwd)/tr2-server-events\" git-receive-pack" \
origin HEAD:new-branch &&
grep \"key\":\"server-sid\" tr2-client-events &&
grep \"key\":\"client-sid\" tr2-server-events
'
done
test_done

View File

@ -792,3 +792,8 @@ void trace2_printf(const char *fmt, ...)
va_end(ap);
}
#endif
const char *trace2_session_id(void)
{
return tr2_sid_get();
}

View File

@ -500,4 +500,6 @@ void trace2_collect_process_info(enum trace2_process_info_reason reason);
} while (0)
#endif
const char *trace2_session_id(void);
#endif /* TRACE2_H */

View File

@ -286,6 +286,8 @@ static struct ref *handshake(struct transport *transport, int for_push,
struct git_transport_data *data = transport->data;
struct ref *refs = NULL;
struct packet_reader reader;
int sid_len;
const char *server_sid;
connect_setup(transport, for_push);
@ -297,6 +299,8 @@ static struct ref *handshake(struct transport *transport, int for_push,
data->version = discover_version(&reader);
switch (data->version) {
case protocol_v2:
if (server_feature_v2("session-id", &server_sid))
trace2_data_string("transfer", NULL, "server-sid", server_sid);
if (must_list_refs)
get_remote_refs(data->fd[1], &reader, &refs, for_push,
ref_prefixes,
@ -310,6 +314,12 @@ static struct ref *handshake(struct transport *transport, int for_push,
for_push ? REF_NORMAL : 0,
&data->extra_have,
&data->shallow);
server_sid = server_feature_value("session-id", &sid_len);
if (server_sid) {
char *sid = xstrndup(server_sid, sid_len);
trace2_data_string("transfer", NULL, "server-sid", sid);
free(sid);
}
break;
case protocol_unknown_version:
BUG("unknown protocol version");

View File

@ -110,6 +110,7 @@ struct upload_pack_data {
unsigned done : 1; /* v2 only */
unsigned allow_ref_in_want : 1; /* v2 only */
unsigned allow_sideband_all : 1; /* v2 only */
unsigned advertise_sid : 1;
};
static void upload_pack_data_init(struct upload_pack_data *data)
@ -141,6 +142,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
packet_writer_init(&data->writer, 1);
data->keepalive = 5;
data->advertise_sid = 0;
}
static void upload_pack_data_clear(struct upload_pack_data *data)
@ -1057,6 +1059,7 @@ static void receive_needs(struct upload_pack_data *data,
const char *features;
struct object_id oid_buf;
const char *arg;
int feature_len;
reset_timeout(data->timeout);
if (packet_reader_read(reader) != PACKET_READ_NORMAL)
@ -1109,6 +1112,13 @@ static void receive_needs(struct upload_pack_data *data,
parse_feature_request(features, "filter"))
data->filter_capability_requested = 1;
arg = parse_feature_value(features, "session-id", &feature_len, NULL);
if (arg) {
char *client_sid = xstrndup(arg, feature_len);
trace2_data_string("transfer", NULL, "client-sid", client_sid);
free(client_sid);
}
o = parse_object(the_repository, &oid_buf);
if (!o) {
packet_writer_error(&data->writer,
@ -1179,6 +1189,11 @@ static void format_symref_info(struct strbuf *buf, struct string_list *symref)
strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
}
static void format_session_id(struct strbuf *buf, struct upload_pack_data *d) {
if (d->advertise_sid)
strbuf_addf(buf, " session-id=%s", trace2_session_id());
}
static int send_ref(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
@ -1194,9 +1209,11 @@ static int send_ref(const char *refname, const struct object_id *oid,
if (capabilities) {
struct strbuf symref_info = STRBUF_INIT;
struct strbuf session_id = STRBUF_INIT;
format_symref_info(&symref_info, &data->symref);
packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s object-format=%s agent=%s\n",
format_session_id(&session_id, data);
packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s%s object-format=%s agent=%s\n",
oid_to_hex(oid), refname_nons,
0, capabilities,
(data->allow_uor & ALLOW_TIP_SHA1) ?
@ -1206,9 +1223,11 @@ static int send_ref(const char *refname, const struct object_id *oid,
data->stateless_rpc ? " no-done" : "",
symref_info.buf,
data->allow_filter ? " filter" : "",
session_id.buf,
the_hash_algo->name,
git_user_agent_sanitized());
strbuf_release(&symref_info);
strbuf_release(&session_id);
} else {
packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
}
@ -1300,6 +1319,8 @@ static int upload_pack_config(const char *var, const char *value, void *cb_data)
data->allow_sideband_all = git_config_bool(var, value);
} else if (!strcmp("core.precomposeunicode", var)) {
precomposed_unicode = git_config_bool(var, value);
} else if (!strcmp("transfer.advertisesid", var)) {
data->advertise_sid = git_config_bool(var, value);
}
if (current_config_scope() != CONFIG_SCOPE_LOCAL &&