5
5
#include "http.h"
6
6
#include "exec_cmd.h"
7
7
#include "run-command.h"
8
+ #include "pkt-line.h"
8
9
9
10
static struct remote * remote ;
10
11
static const char * url ;
@@ -75,21 +76,46 @@ static int set_option(const char *name, const char *value)
75
76
}
76
77
}
77
78
78
- static struct ref * get_refs (void )
79
+ struct discovery {
80
+ const char * service ;
81
+ char * buf_alloc ;
82
+ char * buf ;
83
+ size_t len ;
84
+ unsigned proto_git : 1 ;
85
+ };
86
+ static struct discovery * last_discovery ;
87
+
88
+ static void free_discovery (struct discovery * d )
89
+ {
90
+ if (d ) {
91
+ if (d == last_discovery )
92
+ last_discovery = NULL ;
93
+ free (d -> buf_alloc );
94
+ free (d );
95
+ }
96
+ }
97
+
98
+ static struct discovery * discover_refs (const char * service )
79
99
{
80
100
struct strbuf buffer = STRBUF_INIT ;
81
- char * data , * start , * mid ;
82
- char * ref_name ;
101
+ struct discovery * last = last_discovery ;
83
102
char * refs_url ;
84
- int i = 0 ;
85
- int http_ret ;
103
+ int http_ret , is_http = 0 ;
86
104
87
- struct ref * refs = NULL ;
88
- struct ref * ref = NULL ;
89
- struct ref * last_ref = NULL ;
105
+ if ( last && ! strcmp ( service , last -> service ))
106
+ return last ;
107
+ free_discovery ( last ) ;
90
108
91
- refs_url = xmalloc (strlen (url ) + 11 );
92
- sprintf (refs_url , "%s/info/refs" , url );
109
+ strbuf_addf (& buffer , "%s/info/refs" , url );
110
+ if (!prefixcmp (url , "http://" ) || !prefixcmp (url , "https://" )) {
111
+ is_http = 1 ;
112
+ if (!strchr (url , '?' ))
113
+ strbuf_addch (& buffer , '?' );
114
+ else
115
+ strbuf_addch (& buffer , '&' );
116
+ strbuf_addf (& buffer , "service=%s" , service );
117
+ }
118
+ refs_url = strbuf_detach (& buffer , NULL );
93
119
94
120
init_walker ();
95
121
http_ret = http_get_strbuf (refs_url , & buffer , HTTP_NO_CACHE );
@@ -104,10 +130,86 @@ static struct ref *get_refs(void)
104
130
die ("HTTP request failed" );
105
131
}
106
132
107
- data = buffer .buf ;
133
+ last = xcalloc (1 , sizeof (* last_discovery ));
134
+ last -> service = service ;
135
+ last -> buf_alloc = strbuf_detach (& buffer , & last -> len );
136
+ last -> buf = last -> buf_alloc ;
137
+
138
+ if (is_http && 5 <= last -> len && last -> buf [4 ] == '#' ) {
139
+ /* smart HTTP response; validate that the service
140
+ * pkt-line matches our request.
141
+ */
142
+ struct strbuf exp = STRBUF_INIT ;
143
+
144
+ if (packet_get_line (& buffer , & last -> buf , & last -> len ) <= 0 )
145
+ die ("%s has invalid packet header" , refs_url );
146
+ if (buffer .len && buffer .buf [buffer .len - 1 ] == '\n' )
147
+ strbuf_setlen (& buffer , buffer .len - 1 );
148
+
149
+ strbuf_addf (& exp , "# service=%s" , service );
150
+ if (strbuf_cmp (& exp , & buffer ))
151
+ die ("invalid server response; got '%s'" , buffer .buf );
152
+ strbuf_release (& exp );
153
+
154
+ /* The header can include additional metadata lines, up
155
+ * until a packet flush marker. Ignore these now, but
156
+ * in the future we might start to scan them.
157
+ */
158
+ strbuf_reset (& buffer );
159
+ while (packet_get_line (& buffer , & last -> buf , & last -> len ) > 0 )
160
+ strbuf_reset (& buffer );
161
+
162
+ last -> proto_git = 1 ;
163
+ }
164
+
165
+ free (refs_url );
166
+ strbuf_release (& buffer );
167
+ last_discovery = last ;
168
+ return last ;
169
+ }
170
+
171
+ static int write_discovery (int fd , void * data )
172
+ {
173
+ struct discovery * heads = data ;
174
+ int err = 0 ;
175
+ if (write_in_full (fd , heads -> buf , heads -> len ) != heads -> len )
176
+ err = 1 ;
177
+ close (fd );
178
+ return err ;
179
+ }
180
+
181
+ static struct ref * parse_git_refs (struct discovery * heads )
182
+ {
183
+ struct ref * list = NULL ;
184
+ struct async async ;
185
+
186
+ memset (& async , 0 , sizeof (async ));
187
+ async .proc = write_discovery ;
188
+ async .data = heads ;
189
+
190
+ if (start_async (& async ))
191
+ die ("cannot start thread to parse advertised refs" );
192
+ get_remote_heads (async .out , & list , 0 , NULL , 0 , NULL );
193
+ close (async .out );
194
+ if (finish_async (& async ))
195
+ die ("ref parsing thread failed" );
196
+ return list ;
197
+ }
198
+
199
+ static struct ref * parse_info_refs (struct discovery * heads )
200
+ {
201
+ char * data , * start , * mid ;
202
+ char * ref_name ;
203
+ int i = 0 ;
204
+
205
+ struct ref * refs = NULL ;
206
+ struct ref * ref = NULL ;
207
+ struct ref * last_ref = NULL ;
208
+
209
+ data = heads -> buf ;
108
210
start = NULL ;
109
211
mid = data ;
110
- while (i < buffer . len ) {
212
+ while (i < heads -> len ) {
111
213
if (!start ) {
112
214
start = & data [i ];
113
215
}
@@ -131,8 +233,7 @@ static struct ref *get_refs(void)
131
233
i ++ ;
132
234
}
133
235
134
- strbuf_release (& buffer );
135
-
236
+ init_walker ();
136
237
ref = alloc_ref ("HEAD" );
137
238
if (!walker -> fetch_ref (walker , ref ) &&
138
239
!resolve_remote_symref (ref , refs )) {
@@ -142,11 +243,23 @@ static struct ref *get_refs(void)
142
243
free (ref );
143
244
}
144
245
145
- strbuf_release (& buffer );
146
- free (refs_url );
147
246
return refs ;
148
247
}
149
248
249
+ static struct ref * get_refs (int for_push )
250
+ {
251
+ struct discovery * heads ;
252
+
253
+ if (for_push )
254
+ heads = discover_refs ("git-receive-pack" );
255
+ else
256
+ heads = discover_refs ("git-upload-pack" );
257
+
258
+ if (heads -> proto_git )
259
+ return parse_git_refs (heads );
260
+ return parse_info_refs (heads );
261
+ }
262
+
150
263
static void output_refs (struct ref * refs )
151
264
{
152
265
struct ref * posn ;
@@ -317,7 +430,8 @@ int main(int argc, const char **argv)
317
430
parse_fetch (& buf );
318
431
319
432
} else if (!strcmp (buf .buf , "list" ) || !prefixcmp (buf .buf , "list " )) {
320
- output_refs (get_refs ());
433
+ int for_push = !!strstr (buf .buf + 4 , "for-push" );
434
+ output_refs (get_refs (for_push ));
321
435
322
436
} else if (!prefixcmp (buf .buf , "push " )) {
323
437
parse_push (& buf );
0 commit comments