@@ -188,7 +188,12 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
188
188
heads -> version = discover_version (& reader );
189
189
switch (heads -> version ) {
190
190
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
+ */
192
197
break ;
193
198
case protocol_v1 :
194
199
case protocol_v0 :
@@ -1085,6 +1090,202 @@ static void parse_push(struct strbuf *buf)
1085
1090
free (specs );
1086
1091
}
1087
1092
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
+
1088
1289
int cmd_main (int argc , const char * * argv )
1089
1290
{
1090
1291
struct strbuf buf = STRBUF_INIT ;
@@ -1153,12 +1354,16 @@ int cmd_main(int argc, const char **argv)
1153
1354
fflush (stdout );
1154
1355
1155
1356
} else if (!strcmp (buf .buf , "capabilities" )) {
1357
+ printf ("stateless-connect\n" );
1156
1358
printf ("fetch\n" );
1157
1359
printf ("option\n" );
1158
1360
printf ("push\n" );
1159
1361
printf ("check-connectivity\n" );
1160
1362
printf ("\n" );
1161
1363
fflush (stdout );
1364
+ } else if (skip_prefix (buf .buf , "stateless-connect " , & arg )) {
1365
+ if (!stateless_connect (arg ))
1366
+ break ;
1162
1367
} else {
1163
1368
error ("remote-curl: unknown command '%s' from git" , buf .buf );
1164
1369
return 1 ;
0 commit comments