Skip to content

Commit 97cc7bc

Browse files
spearcegitster
authored andcommitted
Discover refs via smart HTTP server when available
Instead of loading the cached info/refs, try to use the smart HTTP version when the server supports it. Since the smart variant is actually the pkt-line stream from the start of either upload-pack or receive-pack we need to parse these through get_remote_heads, which requires a background thread to feed its pipe. Signed-off-by: Shawn O. Pearce <[email protected]> CC: Daniel Barkalow <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f5ba2d1 commit 97cc7bc

File tree

1 file changed

+131
-17
lines changed

1 file changed

+131
-17
lines changed

remote-curl.c

Lines changed: 131 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "http.h"
66
#include "exec_cmd.h"
77
#include "run-command.h"
8+
#include "pkt-line.h"
89

910
static struct remote *remote;
1011
static const char *url;
@@ -75,21 +76,46 @@ static int set_option(const char *name, const char *value)
7576
}
7677
}
7778

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)
7999
{
80100
struct strbuf buffer = STRBUF_INIT;
81-
char *data, *start, *mid;
82-
char *ref_name;
101+
struct discovery *last = last_discovery;
83102
char *refs_url;
84-
int i = 0;
85-
int http_ret;
103+
int http_ret, is_http = 0;
86104

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);
90108

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);
93119

94120
init_walker();
95121
http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
@@ -104,10 +130,86 @@ static struct ref *get_refs(void)
104130
die("HTTP request failed");
105131
}
106132

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;
108210
start = NULL;
109211
mid = data;
110-
while (i < buffer.len) {
212+
while (i < heads->len) {
111213
if (!start) {
112214
start = &data[i];
113215
}
@@ -131,8 +233,7 @@ static struct ref *get_refs(void)
131233
i++;
132234
}
133235

134-
strbuf_release(&buffer);
135-
236+
init_walker();
136237
ref = alloc_ref("HEAD");
137238
if (!walker->fetch_ref(walker, ref) &&
138239
!resolve_remote_symref(ref, refs)) {
@@ -142,11 +243,23 @@ static struct ref *get_refs(void)
142243
free(ref);
143244
}
144245

145-
strbuf_release(&buffer);
146-
free(refs_url);
147246
return refs;
148247
}
149248

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+
150263
static void output_refs(struct ref *refs)
151264
{
152265
struct ref *posn;
@@ -317,7 +430,8 @@ int main(int argc, const char **argv)
317430
parse_fetch(&buf);
318431

319432
} 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));
321435

322436
} else if (!prefixcmp(buf.buf, "push ")) {
323437
parse_push(&buf);

0 commit comments

Comments
 (0)