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