Git fork
at reftables-rust 372 lines 9.1 kB view raw
1#include "git-compat-util.h" 2#include "repository.h" 3#include "config.h" 4#include "hash.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" 13#include "promisor-remote.h" 14 15static int advertise_sid = -1; 16static int advertise_object_info = -1; 17static int client_hash_algo = GIT_HASH_SHA1_LEGACY; 18 19static int always_advertise(struct repository *r UNUSED, 20 struct strbuf *value UNUSED) 21{ 22 return 1; 23} 24 25static int agent_advertise(struct repository *r UNUSED, 26 struct strbuf *value) 27{ 28 if (value) 29 strbuf_addstr(value, git_user_agent_sanitized()); 30 return 1; 31} 32 33static int promisor_remote_advertise(struct repository *r, 34 struct strbuf *value) 35{ 36 if (value) { 37 char *info = promisor_remote_info(r); 38 if (!info) 39 return 0; 40 strbuf_addstr(value, info); 41 free(info); 42 } 43 return 1; 44} 45 46static void promisor_remote_receive(struct repository *r, 47 const char *remotes) 48{ 49 mark_promisor_remotes_as_accepted(r, remotes); 50} 51 52 53static int object_format_advertise(struct repository *r, 54 struct strbuf *value) 55{ 56 if (value) 57 strbuf_addstr(value, r->hash_algo->name); 58 return 1; 59} 60 61static void object_format_receive(struct repository *r UNUSED, 62 const char *algo_name) 63{ 64 if (!algo_name) 65 die("object-format capability requires an argument"); 66 67 client_hash_algo = hash_algo_by_name(algo_name); 68 if (client_hash_algo == GIT_HASH_UNKNOWN) 69 die("unknown object format '%s'", algo_name); 70} 71 72static int session_id_advertise(struct repository *r, struct strbuf *value) 73{ 74 if (advertise_sid == -1 && 75 repo_config_get_bool(r, "transfer.advertisesid", &advertise_sid)) 76 advertise_sid = 0; 77 if (!advertise_sid) 78 return 0; 79 if (value) 80 strbuf_addstr(value, trace2_session_id()); 81 return 1; 82} 83 84static void session_id_receive(struct repository *r UNUSED, 85 const char *client_sid) 86{ 87 if (!client_sid) 88 client_sid = ""; 89 trace2_data_string("transfer", NULL, "client-sid", client_sid); 90} 91 92static int object_info_advertise(struct repository *r, struct strbuf *value UNUSED) 93{ 94 if (advertise_object_info == -1 && 95 repo_config_get_bool(r, "transfer.advertiseobjectinfo", 96 &advertise_object_info)) { 97 /* disabled by default */ 98 advertise_object_info = 0; 99 } 100 return advertise_object_info; 101} 102 103struct protocol_capability { 104 /* 105 * The name of the capability. The server uses this name when 106 * advertising this capability, and the client uses this name to 107 * specify this capability. 108 */ 109 const char *name; 110 111 /* 112 * Function queried to see if a capability should be advertised. 113 * Optionally a value can be specified by adding it to 'value'. 114 * If a value is added to 'value', the server will advertise this 115 * capability as "<name>=<value>" instead of "<name>". 116 */ 117 int (*advertise)(struct repository *r, struct strbuf *value); 118 119 /* 120 * Function called when a client requests the capability as a command. 121 * Will be provided a struct packet_reader 'request' which it should 122 * use to read the command specific part of the request. Every command 123 * MUST read until a flush packet is seen before sending a response. 124 * 125 * This field should be NULL for capabilities which are not commands. 126 */ 127 int (*command)(struct repository *r, struct packet_reader *request); 128 129 /* 130 * Function called when a client requests the capability as a 131 * non-command. This may be NULL if the capability does nothing. 132 * 133 * For a capability of the form "foo=bar", the value string points to 134 * the content after the "=" (i.e., "bar"). For simple capabilities 135 * (just "foo"), it is NULL. 136 */ 137 void (*receive)(struct repository *r, const char *value); 138}; 139 140static struct protocol_capability capabilities[] = { 141 { 142 .name = "agent", 143 .advertise = agent_advertise, 144 }, 145 { 146 .name = "ls-refs", 147 .advertise = ls_refs_advertise, 148 .command = ls_refs, 149 }, 150 { 151 .name = "fetch", 152 .advertise = upload_pack_advertise, 153 .command = upload_pack_v2, 154 }, 155 { 156 .name = "server-option", 157 .advertise = always_advertise, 158 }, 159 { 160 .name = "object-format", 161 .advertise = object_format_advertise, 162 .receive = object_format_receive, 163 }, 164 { 165 .name = "session-id", 166 .advertise = session_id_advertise, 167 .receive = session_id_receive, 168 }, 169 { 170 .name = "object-info", 171 .advertise = object_info_advertise, 172 .command = cap_object_info, 173 }, 174 { 175 .name = "bundle-uri", 176 .advertise = bundle_uri_advertise, 177 .command = bundle_uri_command, 178 }, 179 { 180 .name = "promisor-remote", 181 .advertise = promisor_remote_advertise, 182 .receive = promisor_remote_receive, 183 }, 184}; 185 186void protocol_v2_advertise_capabilities(struct repository *r) 187{ 188 struct strbuf capability = STRBUF_INIT; 189 struct strbuf value = STRBUF_INIT; 190 191 /* serve by default supports v2 */ 192 packet_write_fmt(1, "version 2\n"); 193 194 for (size_t i = 0; i < ARRAY_SIZE(capabilities); i++) { 195 struct protocol_capability *c = &capabilities[i]; 196 197 if (c->advertise(r, &value)) { 198 strbuf_addstr(&capability, c->name); 199 200 if (value.len) { 201 strbuf_addch(&capability, '='); 202 strbuf_addbuf(&capability, &value); 203 } 204 205 strbuf_addch(&capability, '\n'); 206 packet_write(1, capability.buf, capability.len); 207 } 208 209 strbuf_reset(&capability); 210 strbuf_reset(&value); 211 } 212 213 packet_flush(1); 214 strbuf_release(&capability); 215 strbuf_release(&value); 216} 217 218static struct protocol_capability *get_capability(const char *key, const char **value) 219{ 220 if (!key) 221 return NULL; 222 223 for (size_t i = 0; i < ARRAY_SIZE(capabilities); i++) { 224 struct protocol_capability *c = &capabilities[i]; 225 const char *out; 226 if (!skip_prefix(key, c->name, &out)) 227 continue; 228 if (!*out) { 229 *value = NULL; 230 return c; 231 } 232 if (*out++ == '=') { 233 *value = out; 234 return c; 235 } 236 } 237 238 return NULL; 239} 240 241static int receive_client_capability(struct repository *r, const char *key) 242{ 243 const char *value; 244 const struct protocol_capability *c = get_capability(key, &value); 245 246 if (!c || c->command || !c->advertise(r, NULL)) 247 return 0; 248 249 if (c->receive) 250 c->receive(r, value); 251 return 1; 252} 253 254static int parse_command(struct repository *r, const char *key, struct protocol_capability **command) 255{ 256 const char *out; 257 258 if (skip_prefix(key, "command=", &out)) { 259 const char *value; 260 struct protocol_capability *cmd = get_capability(out, &value); 261 262 if (*command) 263 die("command '%s' requested after already requesting command '%s'", 264 out, (*command)->name); 265 if (!cmd || !cmd->advertise(r, NULL) || !cmd->command || value) 266 die("invalid command '%s'", out); 267 268 *command = cmd; 269 return 1; 270 } 271 272 return 0; 273} 274 275enum request_state { 276 PROCESS_REQUEST_KEYS, 277 PROCESS_REQUEST_DONE, 278}; 279 280static int process_request(struct repository *r) 281{ 282 enum request_state state = PROCESS_REQUEST_KEYS; 283 struct packet_reader reader; 284 int seen_capability_or_command = 0; 285 struct protocol_capability *command = NULL; 286 287 packet_reader_init(&reader, 0, NULL, 0, 288 PACKET_READ_CHOMP_NEWLINE | 289 PACKET_READ_GENTLE_ON_EOF | 290 PACKET_READ_DIE_ON_ERR_PACKET); 291 292 /* 293 * Check to see if the client closed their end before sending another 294 * request. If so we can terminate the connection. 295 */ 296 if (packet_reader_peek(&reader) == PACKET_READ_EOF) 297 return 1; 298 reader.options &= ~PACKET_READ_GENTLE_ON_EOF; 299 300 while (state != PROCESS_REQUEST_DONE) { 301 switch (packet_reader_peek(&reader)) { 302 case PACKET_READ_EOF: 303 BUG("Should have already died when seeing EOF"); 304 case PACKET_READ_NORMAL: 305 if (parse_command(r, reader.line, &command) || 306 receive_client_capability(r, reader.line)) 307 seen_capability_or_command = 1; 308 else 309 die("unknown capability '%s'", reader.line); 310 311 /* Consume the peeked line */ 312 packet_reader_read(&reader); 313 break; 314 case PACKET_READ_FLUSH: 315 /* 316 * If no command and no keys were given then the client 317 * wanted to terminate the connection. 318 */ 319 if (!seen_capability_or_command) 320 return 1; 321 322 /* 323 * The flush packet isn't consume here like it is in 324 * the other parts of this switch statement. This is 325 * so that the command can read the flush packet and 326 * see the end of the request in the same way it would 327 * if command specific arguments were provided after a 328 * delim packet. 329 */ 330 state = PROCESS_REQUEST_DONE; 331 break; 332 case PACKET_READ_DELIM: 333 /* Consume the peeked line */ 334 packet_reader_read(&reader); 335 336 state = PROCESS_REQUEST_DONE; 337 break; 338 case PACKET_READ_RESPONSE_END: 339 BUG("unexpected response end packet"); 340 } 341 } 342 343 if (!command) 344 die("no command requested"); 345 346 if (client_hash_algo != hash_algo_by_ptr(r->hash_algo)) 347 die("mismatched object format: server %s; client %s", 348 r->hash_algo->name, 349 hash_algos[client_hash_algo].name); 350 351 command->command(r, &reader); 352 353 return 0; 354} 355 356void protocol_v2_serve_loop(struct repository *r, int stateless_rpc) 357{ 358 if (!stateless_rpc) 359 protocol_v2_advertise_capabilities(r); 360 361 /* 362 * If stateless-rpc was requested then exit after 363 * a single request/response exchange 364 */ 365 if (stateless_rpc) { 366 process_request(r); 367 } else { 368 for (;;) 369 if (process_request(r)) 370 break; 371 } 372}