trailer: add unit tests for trailer iterator
[alt-git.git] / serve.c
blobaa651b73e9b7a3378c51f7c92533a6dd0f82ce73
1 #include "git-compat-util.h"
2 #include "repository.h"
3 #include "config.h"
4 #include "hash-ll.h"
5 #include "pkt-line.h"
6 #include "version.h"
7 #include "ls-refs.h"
8 #include "protocol-caps.h"
9 #include "serve.h"
10 #include "upload-pack.h"
11 #include "bundle-uri.h"
12 #include "trace2.h"
14 static int advertise_sid = -1;
15 static int advertise_object_info = -1;
16 static int client_hash_algo = GIT_HASH_SHA1;
18 static int always_advertise(struct repository *r UNUSED,
19 struct strbuf *value UNUSED)
21 return 1;
24 static int agent_advertise(struct repository *r UNUSED,
25 struct strbuf *value)
27 if (value)
28 strbuf_addstr(value, git_user_agent_sanitized());
29 return 1;
32 static int object_format_advertise(struct repository *r,
33 struct strbuf *value)
35 if (value)
36 strbuf_addstr(value, r->hash_algo->name);
37 return 1;
40 static void object_format_receive(struct repository *r UNUSED,
41 const char *algo_name)
43 if (!algo_name)
44 die("object-format capability requires an argument");
46 client_hash_algo = hash_algo_by_name(algo_name);
47 if (client_hash_algo == GIT_HASH_UNKNOWN)
48 die("unknown object format '%s'", algo_name);
51 static int session_id_advertise(struct repository *r, struct strbuf *value)
53 if (advertise_sid == -1 &&
54 repo_config_get_bool(r, "transfer.advertisesid", &advertise_sid))
55 advertise_sid = 0;
56 if (!advertise_sid)
57 return 0;
58 if (value)
59 strbuf_addstr(value, trace2_session_id());
60 return 1;
63 static void session_id_receive(struct repository *r UNUSED,
64 const char *client_sid)
66 if (!client_sid)
67 client_sid = "";
68 trace2_data_string("transfer", NULL, "client-sid", client_sid);
71 static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED)
73 if (advertise_object_info == -1 &&
74 repo_config_get_bool(r, "transfer.advertiseobjectinfo",
75 &advertise_object_info)) {
76 /* disabled by default */
77 advertise_object_info = 0;
79 return advertise_object_info;
82 struct protocol_capability {
84 * The name of the capability. The server uses this name when
85 * advertising this capability, and the client uses this name to
86 * specify this capability.
88 const char *name;
91 * Function queried to see if a capability should be advertised.
92 * Optionally a value can be specified by adding it to 'value'.
93 * If a value is added to 'value', the server will advertise this
94 * capability as "<name>=<value>" instead of "<name>".
96 int (*advertise)(struct repository *r, struct strbuf *value);
99 * Function called when a client requests the capability as a command.
100 * Will be provided a struct packet_reader 'request' which it should
101 * use to read the command specific part of the request. Every command
102 * MUST read until a flush packet is seen before sending a response.
104 * This field should be NULL for capabilities which are not commands.
106 int (*command)(struct repository *r, struct packet_reader *request);
109 * Function called when a client requests the capability as a
110 * non-command. This may be NULL if the capability does nothing.
112 * For a capability of the form "foo=bar", the value string points to
113 * the content after the "=" (i.e., "bar"). For simple capabilities
114 * (just "foo"), it is NULL.
116 void (*receive)(struct repository *r, const char *value);
119 static struct protocol_capability capabilities[] = {
121 .name = "agent",
122 .advertise = agent_advertise,
125 .name = "ls-refs",
126 .advertise = ls_refs_advertise,
127 .command = ls_refs,
130 .name = "fetch",
131 .advertise = upload_pack_advertise,
132 .command = upload_pack_v2,
135 .name = "server-option",
136 .advertise = always_advertise,
139 .name = "object-format",
140 .advertise = object_format_advertise,
141 .receive = object_format_receive,
144 .name = "session-id",
145 .advertise = session_id_advertise,
146 .receive = session_id_receive,
149 .name = "object-info",
150 .advertise = object_info_advertise,
151 .command = cap_object_info,
154 .name = "bundle-uri",
155 .advertise = bundle_uri_advertise,
156 .command = bundle_uri_command,
160 void protocol_v2_advertise_capabilities(void)
162 struct strbuf capability = STRBUF_INIT;
163 struct strbuf value = STRBUF_INIT;
164 int i;
166 /* serve by default supports v2 */
167 packet_write_fmt(1, "version 2\n");
169 for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
170 struct protocol_capability *c = &capabilities[i];
172 if (c->advertise(the_repository, &value)) {
173 strbuf_addstr(&capability, c->name);
175 if (value.len) {
176 strbuf_addch(&capability, '=');
177 strbuf_addbuf(&capability, &value);
180 strbuf_addch(&capability, '\n');
181 packet_write(1, capability.buf, capability.len);
184 strbuf_reset(&capability);
185 strbuf_reset(&value);
188 packet_flush(1);
189 strbuf_release(&capability);
190 strbuf_release(&value);
193 static struct protocol_capability *get_capability(const char *key, const char **value)
195 int i;
197 if (!key)
198 return NULL;
200 for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
201 struct protocol_capability *c = &capabilities[i];
202 const char *out;
203 if (!skip_prefix(key, c->name, &out))
204 continue;
205 if (!*out) {
206 *value = NULL;
207 return c;
209 if (*out++ == '=') {
210 *value = out;
211 return c;
215 return NULL;
218 static int receive_client_capability(const char *key)
220 const char *value;
221 const struct protocol_capability *c = get_capability(key, &value);
223 if (!c || c->command || !c->advertise(the_repository, NULL))
224 return 0;
226 if (c->receive)
227 c->receive(the_repository, value);
228 return 1;
231 static int parse_command(const char *key, struct protocol_capability **command)
233 const char *out;
235 if (skip_prefix(key, "command=", &out)) {
236 const char *value;
237 struct protocol_capability *cmd = get_capability(out, &value);
239 if (*command)
240 die("command '%s' requested after already requesting command '%s'",
241 out, (*command)->name);
242 if (!cmd || !cmd->advertise(the_repository, NULL) || !cmd->command || value)
243 die("invalid command '%s'", out);
245 *command = cmd;
246 return 1;
249 return 0;
252 enum request_state {
253 PROCESS_REQUEST_KEYS,
254 PROCESS_REQUEST_DONE,
257 static int process_request(void)
259 enum request_state state = PROCESS_REQUEST_KEYS;
260 struct packet_reader reader;
261 int seen_capability_or_command = 0;
262 struct protocol_capability *command = NULL;
264 packet_reader_init(&reader, 0, NULL, 0,
265 PACKET_READ_CHOMP_NEWLINE |
266 PACKET_READ_GENTLE_ON_EOF |
267 PACKET_READ_DIE_ON_ERR_PACKET);
270 * Check to see if the client closed their end before sending another
271 * request. If so we can terminate the connection.
273 if (packet_reader_peek(&reader) == PACKET_READ_EOF)
274 return 1;
275 reader.options &= ~PACKET_READ_GENTLE_ON_EOF;
277 while (state != PROCESS_REQUEST_DONE) {
278 switch (packet_reader_peek(&reader)) {
279 case PACKET_READ_EOF:
280 BUG("Should have already died when seeing EOF");
281 case PACKET_READ_NORMAL:
282 if (parse_command(reader.line, &command) ||
283 receive_client_capability(reader.line))
284 seen_capability_or_command = 1;
285 else
286 die("unknown capability '%s'", reader.line);
288 /* Consume the peeked line */
289 packet_reader_read(&reader);
290 break;
291 case PACKET_READ_FLUSH:
293 * If no command and no keys were given then the client
294 * wanted to terminate the connection.
296 if (!seen_capability_or_command)
297 return 1;
300 * The flush packet isn't consume here like it is in
301 * the other parts of this switch statement. This is
302 * so that the command can read the flush packet and
303 * see the end of the request in the same way it would
304 * if command specific arguments were provided after a
305 * delim packet.
307 state = PROCESS_REQUEST_DONE;
308 break;
309 case PACKET_READ_DELIM:
310 /* Consume the peeked line */
311 packet_reader_read(&reader);
313 state = PROCESS_REQUEST_DONE;
314 break;
315 case PACKET_READ_RESPONSE_END:
316 BUG("unexpected response end packet");
320 if (!command)
321 die("no command requested");
323 if (client_hash_algo != hash_algo_by_ptr(the_repository->hash_algo))
324 die("mismatched object format: server %s; client %s\n",
325 the_repository->hash_algo->name,
326 hash_algos[client_hash_algo].name);
328 command->command(the_repository, &reader);
330 return 0;
333 void protocol_v2_serve_loop(int stateless_rpc)
335 if (!stateless_rpc)
336 protocol_v2_advertise_capabilities();
339 * If stateless-rpc was requested then exit after
340 * a single request/response exchange
342 if (stateless_rpc) {
343 process_request();
344 } else {
345 for (;;)
346 if (process_request())
347 break;