serve.con commit commit-graph write: add progress output (7b0f229)
   1#include "cache.h"
   2#include "repository.h"
   3#include "config.h"
   4#include "pkt-line.h"
   5#include "version.h"
   6#include "argv-array.h"
   7#include "ls-refs.h"
   8#include "serve.h"
   9#include "upload-pack.h"
  10
  11static int always_advertise(struct repository *r,
  12                            struct strbuf *value)
  13{
  14        return 1;
  15}
  16
  17static int agent_advertise(struct repository *r,
  18                           struct strbuf *value)
  19{
  20        if (value)
  21                strbuf_addstr(value, git_user_agent_sanitized());
  22        return 1;
  23}
  24
  25struct protocol_capability {
  26        /*
  27         * The name of the capability.  The server uses this name when
  28         * advertising this capability, and the client uses this name to
  29         * specify this capability.
  30         */
  31        const char *name;
  32
  33        /*
  34         * Function queried to see if a capability should be advertised.
  35         * Optionally a value can be specified by adding it to 'value'.
  36         * If a value is added to 'value', the server will advertise this
  37         * capability as "<name>=<value>" instead of "<name>".
  38         */
  39        int (*advertise)(struct repository *r, struct strbuf *value);
  40
  41        /*
  42         * Function called when a client requests the capability as a command.
  43         * The function will be provided the capabilities requested via 'keys'
  44         * as well as a struct packet_reader 'request' which the command should
  45         * use to read the command specific part of the request.  Every command
  46         * MUST read until a flush packet is seen before sending a response.
  47         *
  48         * This field should be NULL for capabilities which are not commands.
  49         */
  50        int (*command)(struct repository *r,
  51                       struct argv_array *keys,
  52                       struct packet_reader *request);
  53};
  54
  55static struct protocol_capability capabilities[] = {
  56        { "agent", agent_advertise, NULL },
  57        { "ls-refs", always_advertise, ls_refs },
  58        { "fetch", upload_pack_advertise, upload_pack_v2 },
  59        { "server-option", always_advertise, NULL },
  60};
  61
  62static void advertise_capabilities(void)
  63{
  64        struct strbuf capability = STRBUF_INIT;
  65        struct strbuf value = STRBUF_INIT;
  66        int i;
  67
  68        for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
  69                struct protocol_capability *c = &capabilities[i];
  70
  71                if (c->advertise(the_repository, &value)) {
  72                        strbuf_addstr(&capability, c->name);
  73
  74                        if (value.len) {
  75                                strbuf_addch(&capability, '=');
  76                                strbuf_addbuf(&capability, &value);
  77                        }
  78
  79                        strbuf_addch(&capability, '\n');
  80                        packet_write(1, capability.buf, capability.len);
  81                }
  82
  83                strbuf_reset(&capability);
  84                strbuf_reset(&value);
  85        }
  86
  87        packet_flush(1);
  88        strbuf_release(&capability);
  89        strbuf_release(&value);
  90}
  91
  92static struct protocol_capability *get_capability(const char *key)
  93{
  94        int i;
  95
  96        if (!key)
  97                return NULL;
  98
  99        for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
 100                struct protocol_capability *c = &capabilities[i];
 101                const char *out;
 102                if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
 103                        return c;
 104        }
 105
 106        return NULL;
 107}
 108
 109static int is_valid_capability(const char *key)
 110{
 111        const struct protocol_capability *c = get_capability(key);
 112
 113        return c && c->advertise(the_repository, NULL);
 114}
 115
 116static int is_command(const char *key, struct protocol_capability **command)
 117{
 118        const char *out;
 119
 120        if (skip_prefix(key, "command=", &out)) {
 121                struct protocol_capability *cmd = get_capability(out);
 122
 123                if (*command)
 124                        die("command '%s' requested after already requesting command '%s'",
 125                            out, (*command)->name);
 126                if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
 127                        die("invalid command '%s'", out);
 128
 129                *command = cmd;
 130                return 1;
 131        }
 132
 133        return 0;
 134}
 135
 136int has_capability(const struct argv_array *keys, const char *capability,
 137                   const char **value)
 138{
 139        int i;
 140        for (i = 0; i < keys->argc; i++) {
 141                const char *out;
 142                if (skip_prefix(keys->argv[i], capability, &out) &&
 143                    (!*out || *out == '=')) {
 144                        if (value) {
 145                                if (*out == '=')
 146                                        out++;
 147                                *value = out;
 148                        }
 149                        return 1;
 150                }
 151        }
 152
 153        return 0;
 154}
 155
 156enum request_state {
 157        PROCESS_REQUEST_KEYS,
 158        PROCESS_REQUEST_DONE,
 159};
 160
 161static int process_request(void)
 162{
 163        enum request_state state = PROCESS_REQUEST_KEYS;
 164        struct packet_reader reader;
 165        struct argv_array keys = ARGV_ARRAY_INIT;
 166        struct protocol_capability *command = NULL;
 167
 168        packet_reader_init(&reader, 0, NULL, 0,
 169                           PACKET_READ_CHOMP_NEWLINE |
 170                           PACKET_READ_GENTLE_ON_EOF);
 171
 172        /*
 173         * Check to see if the client closed their end before sending another
 174         * request.  If so we can terminate the connection.
 175         */
 176        if (packet_reader_peek(&reader) == PACKET_READ_EOF)
 177                return 1;
 178        reader.options = PACKET_READ_CHOMP_NEWLINE;
 179
 180        while (state != PROCESS_REQUEST_DONE) {
 181                switch (packet_reader_peek(&reader)) {
 182                case PACKET_READ_EOF:
 183                        BUG("Should have already died when seeing EOF");
 184                case PACKET_READ_NORMAL:
 185                        /* collect request; a sequence of keys and values */
 186                        if (is_command(reader.line, &command) ||
 187                            is_valid_capability(reader.line))
 188                                argv_array_push(&keys, reader.line);
 189                        else
 190                                die("unknown capability '%s'", reader.line);
 191
 192                        /* Consume the peeked line */
 193                        packet_reader_read(&reader);
 194                        break;
 195                case PACKET_READ_FLUSH:
 196                        /*
 197                         * If no command and no keys were given then the client
 198                         * wanted to terminate the connection.
 199                         */
 200                        if (!keys.argc)
 201                                return 1;
 202
 203                        /*
 204                         * The flush packet isn't consume here like it is in
 205                         * the other parts of this switch statement.  This is
 206                         * so that the command can read the flush packet and
 207                         * see the end of the request in the same way it would
 208                         * if command specific arguments were provided after a
 209                         * delim packet.
 210                         */
 211                        state = PROCESS_REQUEST_DONE;
 212                        break;
 213                case PACKET_READ_DELIM:
 214                        /* Consume the peeked line */
 215                        packet_reader_read(&reader);
 216
 217                        state = PROCESS_REQUEST_DONE;
 218                        break;
 219                }
 220        }
 221
 222        if (!command)
 223                die("no command requested");
 224
 225        command->command(the_repository, &keys, &reader);
 226
 227        argv_array_clear(&keys);
 228        return 0;
 229}
 230
 231/* Main serve loop for protocol version 2 */
 232void serve(struct serve_options *options)
 233{
 234        if (options->advertise_capabilities || !options->stateless_rpc) {
 235                /* serve by default supports v2 */
 236                packet_write_fmt(1, "version 2\n");
 237
 238                advertise_capabilities();
 239                /*
 240                 * If only the list of capabilities was requested exit
 241                 * immediately after advertising capabilities
 242                 */
 243                if (options->advertise_capabilities)
 244                        return;
 245        }
 246
 247        /*
 248         * If stateless-rpc was requested then exit after
 249         * a single request/response exchange
 250         */
 251        if (options->stateless_rpc) {
 252                process_request();
 253        } else {
 254                for (;;)
 255                        if (process_request())
 256                                break;
 257        }
 258}