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