@@ -188,7 +188,12 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
188188 heads -> version = discover_version (& reader );
189189 switch (heads -> version ) {
190190 case protocol_v2 :
191- die ("support for protocol v2 not implemented yet" );
191+ /*
192+ * Do nothing. This isn't a list of refs but rather a
193+ * capability advertisement. Client would have run
194+ * 'stateless-connect' so we'll dump this capability listing
195+ * and let them request the refs themselves.
196+ */
192197 break ;
193198 case protocol_v1 :
194199 case protocol_v0 :
@@ -1085,6 +1090,202 @@ static void parse_push(struct strbuf *buf)
10851090 free (specs );
10861091}
10871092
1093+ /*
1094+ * Used to represent the state of a connection to an HTTP server when
1095+ * communicating using git's wire-protocol version 2.
1096+ */
1097+ struct proxy_state {
1098+ char * service_name ;
1099+ char * service_url ;
1100+ struct curl_slist * headers ;
1101+ struct strbuf request_buffer ;
1102+ int in ;
1103+ int out ;
1104+ struct packet_reader reader ;
1105+ size_t pos ;
1106+ int seen_flush ;
1107+ };
1108+
1109+ static void proxy_state_init (struct proxy_state * p , const char * service_name ,
1110+ enum protocol_version version )
1111+ {
1112+ struct strbuf buf = STRBUF_INIT ;
1113+
1114+ memset (p , 0 , sizeof (* p ));
1115+ p -> service_name = xstrdup (service_name );
1116+
1117+ p -> in = 0 ;
1118+ p -> out = 1 ;
1119+ strbuf_init (& p -> request_buffer , 0 );
1120+
1121+ strbuf_addf (& buf , "%s%s" , url .buf , p -> service_name );
1122+ p -> service_url = strbuf_detach (& buf , NULL );
1123+
1124+ p -> headers = http_copy_default_headers ();
1125+
1126+ strbuf_addf (& buf , "Content-Type: application/x-%s-request" , p -> service_name );
1127+ p -> headers = curl_slist_append (p -> headers , buf .buf );
1128+ strbuf_reset (& buf );
1129+
1130+ strbuf_addf (& buf , "Accept: application/x-%s-result" , p -> service_name );
1131+ p -> headers = curl_slist_append (p -> headers , buf .buf );
1132+ strbuf_reset (& buf );
1133+
1134+ p -> headers = curl_slist_append (p -> headers , "Transfer-Encoding: chunked" );
1135+
1136+ /* Add the Git-Protocol header */
1137+ if (get_protocol_http_header (version , & buf ))
1138+ p -> headers = curl_slist_append (p -> headers , buf .buf );
1139+
1140+ packet_reader_init (& p -> reader , p -> in , NULL , 0 ,
1141+ PACKET_READ_GENTLE_ON_EOF );
1142+
1143+ strbuf_release (& buf );
1144+ }
1145+
1146+ static void proxy_state_clear (struct proxy_state * p )
1147+ {
1148+ free (p -> service_name );
1149+ free (p -> service_url );
1150+ curl_slist_free_all (p -> headers );
1151+ strbuf_release (& p -> request_buffer );
1152+ }
1153+
1154+ /*
1155+ * CURLOPT_READFUNCTION callback function.
1156+ * Attempts to copy over a single packet-line at a time into the
1157+ * curl provided buffer.
1158+ */
1159+ static size_t proxy_in (char * buffer , size_t eltsize ,
1160+ size_t nmemb , void * userdata )
1161+ {
1162+ size_t max ;
1163+ struct proxy_state * p = userdata ;
1164+ size_t avail = p -> request_buffer .len - p -> pos ;
1165+
1166+
1167+ if (eltsize != 1 )
1168+ BUG ("curl read callback called with size = %" PRIuMAX " != 1" ,
1169+ (uintmax_t )eltsize );
1170+ max = nmemb ;
1171+
1172+ if (!avail ) {
1173+ if (p -> seen_flush ) {
1174+ p -> seen_flush = 0 ;
1175+ return 0 ;
1176+ }
1177+
1178+ strbuf_reset (& p -> request_buffer );
1179+ switch (packet_reader_read (& p -> reader )) {
1180+ case PACKET_READ_EOF :
1181+ die ("unexpected EOF when reading from parent process" );
1182+ case PACKET_READ_NORMAL :
1183+ packet_buf_write_len (& p -> request_buffer , p -> reader .line ,
1184+ p -> reader .pktlen );
1185+ break ;
1186+ case PACKET_READ_DELIM :
1187+ packet_buf_delim (& p -> request_buffer );
1188+ break ;
1189+ case PACKET_READ_FLUSH :
1190+ packet_buf_flush (& p -> request_buffer );
1191+ p -> seen_flush = 1 ;
1192+ break ;
1193+ }
1194+ p -> pos = 0 ;
1195+ avail = p -> request_buffer .len ;
1196+ }
1197+
1198+ if (max < avail )
1199+ avail = max ;
1200+ memcpy (buffer , p -> request_buffer .buf + p -> pos , avail );
1201+ p -> pos += avail ;
1202+ return avail ;
1203+ }
1204+
1205+ static size_t proxy_out (char * buffer , size_t eltsize ,
1206+ size_t nmemb , void * userdata )
1207+ {
1208+ size_t size ;
1209+ struct proxy_state * p = userdata ;
1210+
1211+ if (eltsize != 1 )
1212+ BUG ("curl read callback called with size = %" PRIuMAX " != 1" ,
1213+ (uintmax_t )eltsize );
1214+ size = nmemb ;
1215+
1216+ write_or_die (p -> out , buffer , size );
1217+ return size ;
1218+ }
1219+
1220+ /* Issues a request to the HTTP server configured in `p` */
1221+ static int proxy_request (struct proxy_state * p )
1222+ {
1223+ struct active_request_slot * slot ;
1224+
1225+ slot = get_active_slot ();
1226+
1227+ curl_easy_setopt (slot -> curl , CURLOPT_NOBODY , 0 );
1228+ curl_easy_setopt (slot -> curl , CURLOPT_POST , 1 );
1229+ curl_easy_setopt (slot -> curl , CURLOPT_URL , p -> service_url );
1230+ curl_easy_setopt (slot -> curl , CURLOPT_HTTPHEADER , p -> headers );
1231+
1232+ /* Setup function to read request from client */
1233+ curl_easy_setopt (slot -> curl , CURLOPT_READFUNCTION , proxy_in );
1234+ curl_easy_setopt (slot -> curl , CURLOPT_READDATA , p );
1235+
1236+ /* Setup function to write server response to client */
1237+ curl_easy_setopt (slot -> curl , CURLOPT_WRITEFUNCTION , proxy_out );
1238+ curl_easy_setopt (slot -> curl , CURLOPT_WRITEDATA , p );
1239+
1240+ if (run_slot (slot , NULL ) != HTTP_OK )
1241+ return -1 ;
1242+
1243+ return 0 ;
1244+ }
1245+
1246+ static int stateless_connect (const char * service_name )
1247+ {
1248+ struct discovery * discover ;
1249+ struct proxy_state p ;
1250+
1251+ /*
1252+ * Run the info/refs request and see if the server supports protocol
1253+ * v2. If and only if the server supports v2 can we successfully
1254+ * establish a stateless connection, otherwise we need to tell the
1255+ * client to fallback to using other transport helper functions to
1256+ * complete their request.
1257+ */
1258+ discover = discover_refs (service_name , 0 );
1259+ if (discover -> version != protocol_v2 ) {
1260+ printf ("fallback\n" );
1261+ fflush (stdout );
1262+ return -1 ;
1263+ } else {
1264+ /* Stateless Connection established */
1265+ printf ("\n" );
1266+ fflush (stdout );
1267+ }
1268+
1269+ proxy_state_init (& p , service_name , discover -> version );
1270+
1271+ /*
1272+ * Dump the capability listing that we got from the server earlier
1273+ * during the info/refs request.
1274+ */
1275+ write_or_die (p .out , discover -> buf , discover -> len );
1276+
1277+ /* Peek the next packet line. Until we see EOF keep sending POSTs */
1278+ while (packet_reader_peek (& p .reader ) != PACKET_READ_EOF ) {
1279+ if (proxy_request (& p )) {
1280+ /* We would have an err here */
1281+ break ;
1282+ }
1283+ }
1284+
1285+ proxy_state_clear (& p );
1286+ return 0 ;
1287+ }
1288+
10881289int cmd_main (int argc , const char * * argv )
10891290{
10901291 struct strbuf buf = STRBUF_INIT ;
@@ -1153,12 +1354,16 @@ int cmd_main(int argc, const char **argv)
11531354 fflush (stdout );
11541355
11551356 } else if (!strcmp (buf .buf , "capabilities" )) {
1357+ printf ("stateless-connect\n" );
11561358 printf ("fetch\n" );
11571359 printf ("option\n" );
11581360 printf ("push\n" );
11591361 printf ("check-connectivity\n" );
11601362 printf ("\n" );
11611363 fflush (stdout );
1364+ } else if (skip_prefix (buf .buf , "stateless-connect " , & arg )) {
1365+ if (!stateless_connect (arg ))
1366+ break ;
11621367 } else {
11631368 error ("remote-curl: unknown command '%s' from git" , buf .buf );
11641369 return 1 ;
0 commit comments