serve.con commit Merge branch 'sg/t5516-fixes' (c831198)
   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};
  60
  61static void advertise_capabilities(void)
  62{
  63        struct strbuf capability = STRBUF_INIT;
  64        struct strbuf value = STRBUF_INIT;
  65        int i;
  66
  67        for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
  68                struct protocol_capability *c = &capabilities[i];
  69
  70                if (c->advertise(the_repository, &value)) {
  71                        strbuf_addstr(&capability, c->name);
  72
  73                        if (value.len) {
  74                                strbuf_addch(&capability, '=');
  75                                strbuf_addbuf(&capability, &value);
  76                        }
  77
  78                        strbuf_addch(&capability, '\n');
  79                        packet_write(1, capability.buf, capability.len);
  80                }
  81
  82                strbuf_reset(&capability);
  83                strbuf_reset(&value);
  84        }
  85
  86        packet_flush(1);
  87        strbuf_release(&capability);
  88        strbuf_release(&value);
  89}
  90
  91static struct protocol_capability *get_capability(const char *key)
  92{
  93        int i;
  94
  95        if (!key)
  96                return NULL;
  97
  98        for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
  99                struct protocol_capability *c = &capabilities[i];
 100                const char *out;
 101                if (skip_prefix(key, c->name, &out) && (!*out || *out == '='))
 102                        return c;
 103        }
 104
 105        return NULL;
 106}
 107
 108static int is_valid_capability(const char *key)
 109{
 110        const struct protocol_capability *c = get_capability(key);
 111
 112        return c && c->advertise(the_repository, NULL);
 113}
 114
 115static int is_command(const char *key, struct protocol_capability **command)
 116{
 117        const char *out;
 118
 119        if (skip_prefix(key, "command=", &out)) {
 120                struct protocol_capability *cmd = get_capability(out);
 121
 122                if (*command)
 123                        die("command '%s' requested after already requesting command '%s'",
 124                            out, (*command)->name);
 125                if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command)
 126                        die("invalid command '%s'", out);
 127
 128                *command = cmd;
 129                return 1;
 130        }
 131
 132        return 0;
 133}
 134
 135int has_capability(const struct argv_array *keys, const char *capability,
 136                   const char **value)
 137{
 138        int i;
 139        for (i = 0; i < keys->argc; i++) {
 140                const char *out;
 141                if (skip_prefix(keys->argv[i], capability, &out) &&
 142                    (!*out || *out == '=')) {
 143                        if (value) {
 144                                if (*out == '=')
 145                                        out++;
 146                                *value = out;
 147                        }
 148                        return 1;
 149                }
 150        }
 151
 152        return 0;
 153}
 154
 155enum request_state {
 156        PROCESS_REQUEST_KEYS,
 157        PROCESS_REQUEST_DONE,
 158};
 159
 160static int process_request(void)
 161{
 162        enum request_state state = PROCESS_REQUEST_KEYS;
 163        struct packet_reader reader;
 164        struct argv_array keys = ARGV_ARRAY_INIT;
 165        struct protocol_capability *command = NULL;
 166
 167        packet_reader_init(&reader, 0, NULL, 0,
 168                           PACKET_READ_CHOMP_NEWLINE |
 169                           PACKET_READ_GENTLE_ON_EOF);
 170
 171        /*
 172         * Check to see if the client closed their end before sending another
 173         * request.  If so we can terminate the connection.
 174         */
 175        if (packet_reader_peek(&reader) == PACKET_READ_EOF)
 176                return 1;
 177        reader.options = PACKET_READ_CHOMP_NEWLINE;
 178
 179        while (state != PROCESS_REQUEST_DONE) {
 180                switch (packet_reader_peek(&reader)) {
 181                case PACKET_READ_EOF:
 182                        BUG("Should have already died when seeing EOF");
 183                case PACKET_READ_NORMAL:
 184                        /* collect request; a sequence of keys and values */
 185                        if (is_command(reader.line, &command) ||
 186                            is_valid_capability(reader.line))
 187                                argv_array_push(&keys, reader.line);
 188                        else
 189                                die("unknown capability '%s'", reader.line);
 190
 191                        /* Consume the peeked line */
 192                        packet_reader_read(&reader);
 193                        break;
 194                case PACKET_READ_FLUSH:
 195                        /*
 196                         * If no command and no keys were given then the client
 197                         * wanted to terminate the connection.
 198                         */
 199                        if (!keys.argc)
 200                                return 1;
 201
 202                        /*
 203                         * The flush packet isn't consume here like it is in
 204                         * the other parts of this switch statement.  This is
 205                         * so that the command can read the flush packet and
 206                         * see the end of the request in the same way it would
 207                         * if command specific arguments were provided after a
 208                         * delim packet.
 209                         */
 210                        state = PROCESS_REQUEST_DONE;
 211                        break;
 212                case PACKET_READ_DELIM:
 213                        /* Consume the peeked line */
 214                        packet_reader_read(&reader);
 215
 216                        state = PROCESS_REQUEST_DONE;
 217                        break;
 218                }
 219        }
 220
 221        if (!command)
 222                die("no command requested");
 223
 224        command->command(the_repository, &keys, &reader);
 225
 226        argv_array_clear(&keys);
 227        return 0;
 228}
 229
 230/* Main serve loop for protocol version 2 */
 231void serve(struct serve_options *options)
 232{
 233        if (options->advertise_capabilities || !options->stateless_rpc) {
 234                /* serve by default supports v2 */
 235                packet_write_fmt(1, "version 2\n");
 236
 237                advertise_capabilities();
 238                /*
 239                 * If only the list of capabilities was requested exit
 240                 * immediately after advertising capabilities
 241                 */
 242                if (options->advertise_capabilities)
 243                        return;
 244        }
 245
 246        /*
 247         * If stateless-rpc was requested then exit after
 248         * a single request/response exchange
 249         */
 250        if (options->stateless_rpc) {
 251                process_request();
 252        } else {
 253                for (;;)
 254                        if (process_request())
 255                                break;
 256        }
 257}