From: Brandon Williams <bmwill@google.com>
To: git@vger.kernel.org
Cc: Brandon Williams <bmwill@google.com>
Subject: [WIP 11/15] serve: introduce git-serve
Date: Mon, 4 Dec 2017 15:58:19 -0800 [thread overview]
Message-ID: <20171204235823.63299-12-bmwill@google.com> (raw)
In-Reply-To: <20171204235823.63299-1-bmwill@google.com>
Introduce git-serve, the base server for protocol version 2.
When connecting to a server supporting protocol version 2, the server
will send a list all of its capabilities and then wait for the client to
send a command request. Some capabilities advertised are 'commands'
which the client can request (push and fetch are examples of such
commands). A command request is comprised of a list of capabilities,
including a command request "command=<command>", a delimiter packet,
followed by a list of parameters for the requested command.
At the end of each command a client can request that another command be
executed or can terminate the connection by sending a flush packet.
Signed-off-by: Brandon Williams <bmwill@google.com>
---
.gitignore | 1 +
Makefile | 2 +
builtin.h | 1 +
builtin/serve.c | 25 ++++++++
git.c | 1 +
serve.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
serve.h | 6 ++
7 files changed, 221 insertions(+)
create mode 100644 builtin/serve.c
create mode 100644 serve.c
create mode 100644 serve.h
diff --git a/.gitignore b/.gitignore
index 833ef3b0b..2d0450c26 100644
--- a/.gitignore
+++ b/.gitignore
@@ -140,6 +140,7 @@
/git-rm
/git-send-email
/git-send-pack
+/git-serve
/git-sh-i18n
/git-sh-i18n--envsubst
/git-sh-setup
diff --git a/Makefile b/Makefile
index 86394b69d..710672cf4 100644
--- a/Makefile
+++ b/Makefile
@@ -862,6 +862,7 @@ LIB_OBJS += revision.o
LIB_OBJS += run-command.o
LIB_OBJS += send-pack.o
LIB_OBJS += sequencer.o
+LIB_OBJS += serve.o
LIB_OBJS += server-info.o
LIB_OBJS += setup.o
LIB_OBJS += sha1-array.o
@@ -995,6 +996,7 @@ BUILTIN_OBJS += builtin/rev-parse.o
BUILTIN_OBJS += builtin/revert.o
BUILTIN_OBJS += builtin/rm.o
BUILTIN_OBJS += builtin/send-pack.o
+BUILTIN_OBJS += builtin/serve.o
BUILTIN_OBJS += builtin/shortlog.o
BUILTIN_OBJS += builtin/show-branch.o
BUILTIN_OBJS += builtin/show-ref.o
diff --git a/builtin.h b/builtin.h
index f332a1257..3f3fdfc28 100644
--- a/builtin.h
+++ b/builtin.h
@@ -215,6 +215,7 @@ extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
extern int cmd_revert(int argc, const char **argv, const char *prefix);
extern int cmd_rm(int argc, const char **argv, const char *prefix);
extern int cmd_send_pack(int argc, const char **argv, const char *prefix);
+extern int cmd_serve(int argc, const char **argv, const char *prefix);
extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
diff --git a/builtin/serve.c b/builtin/serve.c
new file mode 100644
index 000000000..2ecaad3b6
--- /dev/null
+++ b/builtin/serve.c
@@ -0,0 +1,25 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+#include "serve.h"
+
+static char const * const grep_usage[] = {
+ N_("git serve [<options>]"),
+ NULL
+};
+
+int cmd_serve(int argc, const char **argv, const char *prefix)
+{
+
+ struct option options[] = {
+ OPT_END()
+ };
+
+ /* ignore all unknown cmdline switches for now */
+ argc = parse_options(argc, argv, prefix, options, grep_usage,
+ PARSE_OPT_KEEP_DASHDASH |
+ PARSE_OPT_KEEP_UNKNOWN);
+ serve();
+
+ return 0;
+}
diff --git a/git.c b/git.c
index e32e16f2d..527086eaf 100644
--- a/git.c
+++ b/git.c
@@ -457,6 +457,7 @@ static struct cmd_struct commands[] = {
{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
{ "rm", cmd_rm, RUN_SETUP },
{ "send-pack", cmd_send_pack, RUN_SETUP },
+ { "serve", cmd_serve, RUN_SETUP },
{ "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
{ "show", cmd_show, RUN_SETUP },
{ "show-branch", cmd_show_branch, RUN_SETUP },
diff --git a/serve.c b/serve.c
new file mode 100644
index 000000000..476e73b54
--- /dev/null
+++ b/serve.c
@@ -0,0 +1,185 @@
+#include "cache.h"
+#include "repository.h"
+#include "config.h"
+#include "pkt-line.h"
+#include "version.h"
+#include "argv-array.h"
+#include "serve.h"
+
+static int agent_advertise(struct repository *r,
+ struct strbuf *value)
+{
+ strbuf_addstr(value, git_user_agent_sanitized());
+ return 1;
+}
+
+struct protocol_capability {
+ const char *name;
+ int advertised; /* capability was advertised */
+ /* int advertise(struct strbuf *value, struct repository *r) */
+ int (*advertise)(struct repository *r, struct strbuf *value);
+ /* int command(struct repository *r, struct argv_array *keys, struct argv_array *args)*/
+ int (*command)(struct repository *r,
+ struct argv_array *keys,
+ struct argv_array *args);
+};
+
+static struct protocol_capability capabilities[] = {
+ { "agent", 0, agent_advertise, NULL },
+};
+
+static void advertise_capabilities(void)
+{
+ struct strbuf capability = STRBUF_INIT;
+ struct strbuf value = STRBUF_INIT;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
+ struct protocol_capability *c = &capabilities[i];
+
+ c->advertised = c->advertise(the_repository, &value);
+ if (c->advertised) {
+ strbuf_addstr(&capability, c->name);
+
+ if (value.len) {
+ strbuf_addch(&capability, '=');
+ strbuf_addbuf(&capability, &value);
+ }
+
+ strbuf_addch(&capability, '\n');
+ packet_write(1, capability.buf, capability.len);
+ }
+
+ strbuf_reset(&capability);
+ strbuf_reset(&value);
+ }
+
+ packet_flush(1);
+ strbuf_release(&capability);
+ strbuf_release(&value);
+}
+
+static struct protocol_capability *get_capability(const char *key)
+{
+ int i;
+
+ if (!key)
+ return NULL;
+
+ 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 == '='))
+ return c;
+ }
+
+ return NULL;
+}
+
+static int is_valid_capability(const char *key)
+{
+ const struct protocol_capability *c = get_capability(key);
+
+ return c && c->advertised;
+}
+
+static int is_command(const char *key, struct protocol_capability **command)
+{
+ const char *out;
+
+ if (skip_prefix(key, "command=", &out)) {
+ struct protocol_capability *cmd = get_capability(out);
+
+ if (!cmd || !cmd->advertised || !cmd->command)
+ die("invalid cmd '%s'", out);
+ if (*command)
+ die("command already requested");
+
+ *command = cmd;
+ return 1;
+ }
+
+ return 0;
+}
+
+#define PROCESS_REQUEST_KEYS 0
+#define PROCESS_REQUEST_ARGS 1
+#define PROCESS_REQUEST_DONE 2
+
+static int process_request(void)
+{
+ int state = PROCESS_REQUEST_KEYS;
+ struct packet_reader reader;
+ struct argv_array keys = ARGV_ARRAY_INIT;
+ struct argv_array args = ARGV_ARRAY_INIT;
+ struct protocol_capability *command = NULL;
+
+ packet_reader_init(&reader, 0, NULL, 0);
+
+ while (state != PROCESS_REQUEST_DONE) {
+ switch (packet_reader_read(&reader)) {
+ case PACKET_READ_ERROR:
+ BUG("invalid state");
+ case PACKET_READ_NORMAL:
+ break;
+ case PACKET_READ_FLUSH:
+ state = PROCESS_REQUEST_DONE;
+ continue;
+ case PACKET_READ_DELIM:
+ if (state != PROCESS_REQUEST_KEYS)
+ die("protocol error");
+ state = PROCESS_REQUEST_ARGS;
+ /*
+ * maybe include a check to make sure that a
+ * command/capabilities were given.
+ */
+ continue;
+ }
+
+ switch (state) {
+ case PROCESS_REQUEST_KEYS:
+ /* collect request; a sequence of keys and values */
+ if (is_command(reader.line, &command) ||
+ is_valid_capability(reader.line))
+ argv_array_push(&keys, reader.line);
+ break;
+ case PROCESS_REQUEST_ARGS:
+ /* collect arguments for the requested command */
+ argv_array_push(&args, reader.line);
+ break;
+ case PROCESS_REQUEST_DONE:
+ continue;
+ default:
+ BUG("invalid state");
+ }
+ }
+
+ /*
+ * If no command and no keys were given then the client wanted to
+ * terminate the connection.
+ */
+ if (!keys.argc && !args.argc)
+ return 1;
+
+ if (!command)
+ die("no command requested");
+
+ command->command(the_repository, &keys, &args);
+
+ argv_array_clear(&keys);
+ argv_array_clear(&args);
+ return 0;
+}
+
+/* Main serve loop for protocol version 2 */
+void serve(void)
+{
+ /* serve by default supports v2 */
+ packet_write_fmt(1, "version 2\n");
+
+ advertise_capabilities();
+
+ for (;;)
+ if (process_request())
+ break;
+}
diff --git a/serve.h b/serve.h
new file mode 100644
index 000000000..1ed9685ca
--- /dev/null
+++ b/serve.h
@@ -0,0 +1,6 @@
+#ifndef SERVE_H
+#define SERVE_H
+
+extern void serve(void);
+
+#endif /* SERVE_H */
--
2.15.1.424.g9478a66081-goog
next prev parent reply other threads:[~2017-12-04 23:59 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-10-20 17:18 [RFC] protocol version 2 Brandon Williams
2017-10-24 6:48 ` Junio C Hamano
2017-10-24 18:35 ` Brandon Williams
2017-10-25 1:22 ` Junio C Hamano
2017-10-26 0:59 ` Junio C Hamano
2017-10-25 13:09 ` Derrick Stolee
2017-10-25 18:10 ` Brandon Williams
2017-10-28 22:57 ` Philip Oakley
2017-10-31 18:42 ` Brandon Williams
2017-11-10 20:13 ` Jonathan Tan
2017-12-04 23:58 ` [WIP 00/15] " Brandon Williams
2017-12-04 23:58 ` [WIP 01/15] pkt-line: introduce packet_read_with_status Brandon Williams
2017-12-07 20:53 ` Stefan Beller
2017-12-08 18:03 ` Brandon Williams
2017-12-04 23:58 ` [WIP 02/15] pkt-line: introduce struct packet_reader Brandon Williams
2017-12-07 22:01 ` Stefan Beller
2017-12-08 18:11 ` Brandon Williams
2017-12-04 23:58 ` [WIP 03/15] pkt-line: add delim packet support Brandon Williams
2017-12-07 22:30 ` Stefan Beller
2017-12-08 20:08 ` Brandon Williams
2017-12-04 23:58 ` [WIP 04/15] upload-pack: convert to a builtin Brandon Williams
2017-12-06 21:59 ` Junio C Hamano
2017-12-07 16:14 ` Johannes Schindelin
2017-12-08 20:26 ` Junio C Hamano
2017-12-08 20:12 ` Brandon Williams
2017-12-04 23:58 ` [WIP 05/15] upload-pack: factor out processing lines Brandon Williams
2017-12-04 23:58 ` [WIP 06/15] transport: use get_refs_via_connect to get refs Brandon Williams
2017-12-06 22:10 ` Junio C Hamano
2017-12-07 18:40 ` Brandon Williams
2017-12-04 23:58 ` [WIP 07/15] connect: convert get_remote_heads to use struct packet_reader Brandon Williams
2017-12-06 22:39 ` Junio C Hamano
2017-12-08 20:19 ` Brandon Williams
2017-12-04 23:58 ` [WIP 08/15] connect: discover protocol version outside of get_remote_heads Brandon Williams
2017-12-07 18:50 ` Junio C Hamano
2017-12-07 19:04 ` Brandon Williams
2017-12-07 19:30 ` Junio C Hamano
2017-12-08 20:11 ` Brandon Williams
2017-12-04 23:58 ` [WIP 09/15] transport: store protocol version Brandon Williams
2017-12-04 23:58 ` [WIP 10/15] protocol: introduce enum protocol_version value protocol_v2 Brandon Williams
2017-12-04 23:58 ` Brandon Williams [this message]
2017-12-07 23:42 ` [WIP 11/15] serve: introduce git-serve Junio C Hamano
2017-12-08 20:25 ` Brandon Williams
2017-12-04 23:58 ` [WIP 12/15] ls-refs: introduce ls-refs server command Brandon Williams
2017-12-13 16:30 ` Philip Oakley
2017-12-04 23:58 ` [WIP 13/15] connect: request remote refs using v2 Brandon Williams
2017-12-04 23:58 ` [WIP 14/15] upload_pack: introduce fetch server command Brandon Williams
2017-12-04 23:58 ` [WIP 15/15] fetch-pack: perform a fetch using v2 Brandon Williams
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171204235823.63299-12-bmwill@google.com \
--to=bmwill@google.com \
--cc=git@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.