@matheusbsbs / matheus-bagliano / commits / 97cc7bc45c
Fork 0

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 <spearce@spearce.org> CC: Daniel Barkalow <barkalow@iabervon.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Shawn O. Pearce committed Oct 30, 2009 at 17:47 UTC 97cc7bc45c2e076184b84749952b216036a6ecd6
1 file changed +131 -17
remote-curl.c
+131 -17
@@ -5,6 +5,7 @@
5 #include "http.h"
6 #include "exec_cmd.h"
7 #include "run-command.h"
8 +#include "pkt-line.h"
9
10 static struct remote *remote;
11 static const char *url;
@@ -75,21 +76,46 @@ static int set_option(const char *name, const char *value)
76 }
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)
99 {
100 struct strbuf buffer = STRBUF_INIT;
81 - char *data, *start, *mid;
82 - char *ref_name;
101 + struct discovery *last = last_discovery;
102 char *refs_url;
84 - int i = 0;
85 - int http_ret;
103 + int http_ret, is_http = 0;
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);
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);
119
120 init_walker();
121 http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
@@ -104,10 +130,86 @@ static struct ref *get_refs(void)
130 die("HTTP request failed");
131 }
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;
210 start = NULL;
211 mid = data;
110 - while (i < buffer.len) {
212 + while (i < heads->len) {
213 if (!start) {
214 start = &data[i];
215 }
@@ -131,8 +233,7 @@ static struct ref *get_refs(void)
233 i++;
234 }
235
134 - strbuf_release(&buffer);
135 -
236 + init_walker();
237 ref = alloc_ref("HEAD");
238 if (!walker->fetch_ref(walker, ref) &&
239 !resolve_remote_symref(ref, refs)) {
@@ -142,11 +243,23 @@ static struct ref *get_refs(void)
243 free(ref);
244 }
245
145 - strbuf_release(&buffer);
146 - free(refs_url);
246 return refs;
247 }
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 +
263 static void output_refs(struct ref *refs)
264 {
265 struct ref *posn;
@@ -317,7 +430,8 @@ int main(int argc, const char **argv)
430 parse_fetch(&buf);
431
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));
435
436 } else if (!prefixcmp(buf.buf, "push ")) {
437 parse_push(&buf);