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