summaryrefslogtreecommitdiff
path: root/serve.c
diff options
context:
space:
mode:
Diffstat (limited to 'serve.c')
-rw-r--r--serve.c120
1 files changed, 64 insertions, 56 deletions
diff --git a/serve.c b/serve.c
index 1817edc7f5..b3fe9b5126 100644
--- a/serve.c
+++ b/serve.c
@@ -10,6 +10,7 @@
#include "upload-pack.h"
static int advertise_sid = -1;
+static int client_hash_algo = GIT_HASH_SHA1;
static int always_advertise(struct repository *r,
struct strbuf *value)
@@ -33,6 +34,17 @@ static int object_format_advertise(struct repository *r,
return 1;
}
+static void object_format_receive(struct repository *r,
+ const char *algo_name)
+{
+ if (!algo_name)
+ die("object-format capability requires an argument");
+
+ client_hash_algo = hash_algo_by_name(algo_name);
+ if (client_hash_algo == GIT_HASH_UNKNOWN)
+ die("unknown object format '%s'", algo_name);
+}
+
static int session_id_advertise(struct repository *r, struct strbuf *value)
{
if (advertise_sid == -1 &&
@@ -45,6 +57,14 @@ static int session_id_advertise(struct repository *r, struct strbuf *value)
return 1;
}
+static void session_id_receive(struct repository *r,
+ const char *client_sid)
+{
+ if (!client_sid)
+ client_sid = "";
+ trace2_data_string("transfer", NULL, "client-sid", client_sid);
+}
+
struct protocol_capability {
/*
* The name of the capability. The server uses this name when
@@ -70,6 +90,16 @@ struct protocol_capability {
* This field should be NULL for capabilities which are not commands.
*/
int (*command)(struct repository *r, struct packet_reader *request);
+
+ /*
+ * Function called when a client requests the capability as a
+ * non-command. This may be NULL if the capability does nothing.
+ *
+ * For a capability of the form "foo=bar", the value string points to
+ * the content after the "=" (i.e., "bar"). For simple capabilities
+ * (just "foo"), it is NULL.
+ */
+ void (*receive)(struct repository *r, const char *value);
};
static struct protocol_capability capabilities[] = {
@@ -94,10 +124,12 @@ static struct protocol_capability capabilities[] = {
{
.name = "object-format",
.advertise = object_format_advertise,
+ .receive = object_format_receive,
},
{
.name = "session-id",
.advertise = session_id_advertise,
+ .receive = session_id_receive,
},
{
.name = "object-info",
@@ -139,7 +171,7 @@ void protocol_v2_advertise_capabilities(void)
strbuf_release(&value);
}
-static struct protocol_capability *get_capability(const char *key)
+static struct protocol_capability *get_capability(const char *key, const char **value)
{
int i;
@@ -149,31 +181,46 @@ static struct protocol_capability *get_capability(const char *key)
for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
struct protocol_capability *c = &capabilities[i];
const char *out;
- if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
+ if (!skip_prefix(key, c->name, &out))
+ continue;
+ if (!*out) {
+ *value = NULL;
return c;
+ }
+ if (*out++ == '=') {
+ *value = out;
+ return c;
+ }
}
return NULL;
}
-static int is_valid_capability(const char *key)
+static int receive_client_capability(const char *key)
{
- const struct protocol_capability *c = get_capability(key);
+ const char *value;
+ const struct protocol_capability *c = get_capability(key, &value);
- return c && c->advertise(the_repository, NULL);
+ if (!c || c->command || !c->advertise(the_repository, NULL))
+ return 0;
+
+ if (c->receive)
+ c->receive(the_repository, value);
+ return 1;
}
-static int is_command(const char *key, struct protocol_capability **command)
+static int parse_command(const char *key, struct protocol_capability **command)
{
const char *out;
if (skip_prefix(key, "command=", &out)) {
- struct protocol_capability *cmd = get_capability(out);
+ const char *value;
+ struct protocol_capability *cmd = get_capability(out, &value);
if (*command)
die("command '%s' requested after already requesting command '%s'",
out, (*command)->name);
- if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
+ if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command || value)
die("invalid command '%s'", out);
*command = cmd;
@@ -183,42 +230,6 @@ static int is_command(const char *key, struct protocol_capability **command)
return 0;
}
-static int has_capability(const struct strvec *keys, const char *capability,
- const char **value)
-{
- int i;
- for (i = 0; i < keys->nr; i++) {
- const char *out;
- if (skip_prefix(keys->v[i], capability, &out) &&
- (!*out || *out == '=')) {
- if (value) {
- if (*out == '=')
- out++;
- *value = out;
- }
- return 1;
- }
- }
-
- return 0;
-}
-
-static void check_algorithm(struct repository *r, struct strvec *keys)
-{
- int client = GIT_HASH_SHA1, server = hash_algo_by_ptr(r->hash_algo);
- const char *algo_name;
-
- if (has_capability(keys, "object-format", &algo_name)) {
- client = hash_algo_by_name(algo_name);
- if (client == GIT_HASH_UNKNOWN)
- die("unknown object format '%s'", algo_name);
- }
-
- if (client != server)
- die("mismatched object format: server %s; client %s\n",
- r->hash_algo->name, hash_algos[client].name);
-}
-
enum request_state {
PROCESS_REQUEST_KEYS,
PROCESS_REQUEST_DONE,
@@ -228,9 +239,8 @@ static int process_request(void)
{
enum request_state state = PROCESS_REQUEST_KEYS;
struct packet_reader reader;
- struct strvec keys = STRVEC_INIT;
+ int seen_capability_or_command = 0;
struct protocol_capability *command = NULL;
- const char *client_sid;
packet_reader_init(&reader, 0, NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
@@ -250,10 +260,9 @@ static int process_request(void)
case PACKET_READ_EOF:
BUG("Should have already died when seeing EOF");
case PACKET_READ_NORMAL:
- /* collect request; a sequence of keys and values */
- if (is_command(reader.line, &command) ||
- is_valid_capability(reader.line))
- strvec_push(&keys, reader.line);
+ if (parse_command(reader.line, &command) ||
+ receive_client_capability(reader.line))
+ seen_capability_or_command = 1;
else
die("unknown capability '%s'", reader.line);
@@ -265,7 +274,7 @@ static int process_request(void)
* If no command and no keys were given then the client
* wanted to terminate the connection.
*/
- if (!keys.nr)
+ if (!seen_capability_or_command)
return 1;
/*
@@ -292,14 +301,13 @@ static int process_request(void)
if (!command)
die("no command requested");
- check_algorithm(the_repository, &keys);
-
- if (has_capability(&keys, "session-id", &client_sid))
- trace2_data_string("transfer", NULL, "client-sid", client_sid);
+ if (client_hash_algo != hash_algo_by_ptr(the_repository->hash_algo))
+ die("mismatched object format: server %s; client %s\n",
+ the_repository->hash_algo->name,
+ hash_algos[client_hash_algo].name);
command->command(the_repository, &reader);
- strvec_clear(&keys);
return 0;
}