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