serve.con commit t5703: run all non-httpd-specific tests before sourcing 'lib-httpd.sh' (7f005b0)
   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                           PACKET_READ_DIE_ON_ERR_PACKET);
 172
 173        /*
 174         * Check to see if the client closed their end before sending another
 175         * request.  If so we can terminate the connection.
 176         */
 177        if (packet_reader_peek(&reader) == PACKET_READ_EOF)
 178                return 1;
 179        reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
 180
 181        while (state != PROCESS_REQUEST_DONE) {
 182                switch (packet_reader_peek(&reader)) {
 183                case PACKET_READ_EOF:
 184                        BUG("Should have already died when seeing EOF");
 185                case PACKET_READ_NORMAL:
 186                        /* collect request; a sequence of keys and values */
 187                        if (is_command(reader.line, &command) ||
 188                            is_valid_capability(reader.line))
 189                                argv_array_push(&keys, reader.line);
 190                        else
 191                                die("unknown capability '%s'", reader.line);
 192
 193                        /* Consume the peeked line */
 194                        packet_reader_read(&reader);
 195                        break;
 196                case PACKET_READ_FLUSH:
 197                        /*
 198                         * If no command and no keys were given then the client
 199                         * wanted to terminate the connection.
 200                         */
 201                        if (!keys.argc)
 202                                return 1;
 203
 204                        /*
 205                         * The flush packet isn't consume here like it is in
 206                         * the other parts of this switch statement.  This is
 207                         * so that the command can read the flush packet and
 208                         * see the end of the request in the same way it would
 209                         * if command specific arguments were provided after a
 210                         * delim packet.
 211                         */
 212                        state = PROCESS_REQUEST_DONE;
 213                        break;
 214                case PACKET_READ_DELIM:
 215                        /* Consume the peeked line */
 216                        packet_reader_read(&reader);
 217
 218                        state = PROCESS_REQUEST_DONE;
 219                        break;
 220                }
 221        }
 222
 223        if (!command)
 224                die("no command requested");
 225
 226        command->command(the_repository, &keys, &reader);
 227
 228        argv_array_clear(&keys);
 229        return 0;
 230}
 231
 232/* Main serve loop for protocol version 2 */
 233void serve(struct serve_options *options)
 234{
 235        if (options->advertise_capabilities || !options->stateless_rpc) {
 236                /* serve by default supports v2 */
 237                packet_write_fmt(1, "version 2\n");
 238
 239                advertise_capabilities();
 240                /*
 241                 * If only the list of capabilities was requested exit
 242                 * immediately after advertising capabilities
 243                 */
 244                if (options->advertise_capabilities)
 245                        return;
 246        }
 247
 248        /*
 249         * If stateless-rpc was requested then exit after
 250         * a single request/response exchange
 251         */
 252        if (options->stateless_rpc) {
 253                process_request();
 254        } else {
 255                for (;;)
 256                        if (process_request())
 257                                break;
 258        }
 259}