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 PACKET_READ_DIE_ON_ERR_PACKET); 172 173/* 174 * Check to see if the client closed their end before sending another 175 * request. If so we can terminate the connection. 176 */ 177if(packet_reader_peek(&reader) == PACKET_READ_EOF) 178return1; 179 reader.options &= ~PACKET_READ_GENTLE_ON_EOF; 180 181while(state != PROCESS_REQUEST_DONE) { 182switch(packet_reader_peek(&reader)) { 183case PACKET_READ_EOF: 184BUG("Should have already died when seeing EOF"); 185case PACKET_READ_NORMAL: 186/* collect request; a sequence of keys and values */ 187if(is_command(reader.line, &command) || 188is_valid_capability(reader.line)) 189argv_array_push(&keys, reader.line); 190else 191die("unknown capability '%s'", reader.line); 192 193/* Consume the peeked line */ 194packet_reader_read(&reader); 195break; 196case PACKET_READ_FLUSH: 197/* 198 * If no command and no keys were given then the client 199 * wanted to terminate the connection. 200 */ 201if(!keys.argc) 202return1; 203 204/* 205 * The flush packet isn't consume here like it is in 206 * the other parts of this switch statement. This is 207 * so that the command can read the flush packet and 208 * see the end of the request in the same way it would 209 * if command specific arguments were provided after a 210 * delim packet. 211 */ 212 state = PROCESS_REQUEST_DONE; 213break; 214case PACKET_READ_DELIM: 215/* Consume the peeked line */ 216packet_reader_read(&reader); 217 218 state = PROCESS_REQUEST_DONE; 219break; 220} 221} 222 223if(!command) 224die("no command requested"); 225 226 command->command(the_repository, &keys, &reader); 227 228argv_array_clear(&keys); 229return0; 230} 231 232/* Main serve loop for protocol version 2 */ 233voidserve(struct serve_options *options) 234{ 235if(options->advertise_capabilities || !options->stateless_rpc) { 236/* serve by default supports v2 */ 237packet_write_fmt(1,"version 2\n"); 238 239advertise_capabilities(); 240/* 241 * If only the list of capabilities was requested exit 242 * immediately after advertising capabilities 243 */ 244if(options->advertise_capabilities) 245return; 246} 247 248/* 249 * If stateless-rpc was requested then exit after 250 * a single request/response exchange 251 */ 252if(options->stateless_rpc) { 253process_request(); 254}else{ 255for(;;) 256if(process_request()) 257break; 258} 259}