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
List information: http://vger.kernel.org/majordomo-info.html
* 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.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).