summaryrefslogtreecommitdiff
path: root/remote-curl.c
diff options
context:
space:
mode:
Diffstat (limited to 'remote-curl.c')
-rw-r--r--remote-curl.c584
1 files changed, 309 insertions, 275 deletions
diff --git a/remote-curl.c b/remote-curl.c
index 1220dffcdc..a1bf4f68c7 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -16,6 +16,7 @@
#include "send-pack.h"
#include "protocol.h"
#include "quote.h"
+#include "transport.h"
static struct remote *remote;
/* always ends with a trailing slash */
@@ -153,7 +154,7 @@ static int set_option(const char *name, const char *value)
else {
struct strbuf unquoted = STRBUF_INIT;
if (unquote_c_style(&unquoted, value, NULL) < 0)
- die("invalid quoting in push-option value");
+ die(_("invalid quoting in push-option value: '%s'"), value);
string_list_append_nodup(&options.push_options,
strbuf_detach(&unquoted, NULL));
}
@@ -204,7 +205,8 @@ static struct ref *parse_git_refs(struct discovery *heads, int for_push)
packet_reader_init(&reader, -1, heads->buf, heads->len,
PACKET_READ_CHOMP_NEWLINE |
- PACKET_READ_GENTLE_ON_EOF);
+ PACKET_READ_GENTLE_ON_EOF |
+ PACKET_READ_DIE_ON_ERR_PACKET);
heads->version = discover_version(&reader);
switch (heads->version) {
@@ -248,9 +250,9 @@ static struct ref *parse_info_refs(struct discovery *heads)
if (data[i] == '\t')
mid = &data[i];
if (data[i] == '\n') {
- if (mid - start != 40)
- die("%sinfo/refs not valid: is this a git repository?",
- url.buf);
+ if (mid - start != the_hash_algo->hexsz)
+ die(_("%sinfo/refs not valid: is this a git repository?"),
+ transport_anonymize_url(url.buf));
data[i] = 0;
ref_name = mid + 1;
ref = alloc_ref(ref_name);
@@ -330,9 +332,63 @@ static int get_protocol_http_header(enum protocol_version version,
return 0;
}
+static void check_smart_http(struct discovery *d, const char *service,
+ struct strbuf *type)
+{
+ const char *p;
+ struct packet_reader reader;
+
+ /*
+ * If we don't see x-$service-advertisement, then it's not smart-http.
+ * But once we do, we commit to it and assume any other protocol
+ * violations are hard errors.
+ */
+ if (!skip_prefix(type->buf, "application/x-", &p) ||
+ !skip_prefix(p, service, &p) ||
+ strcmp(p, "-advertisement"))
+ return;
+
+ packet_reader_init(&reader, -1, d->buf, d->len,
+ PACKET_READ_CHOMP_NEWLINE |
+ PACKET_READ_DIE_ON_ERR_PACKET);
+ if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
+ die(_("invalid server response; expected service, got flush packet"));
+
+ if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
+ /*
+ * The header can include additional metadata lines, up
+ * until a packet flush marker. Ignore these now, but
+ * in the future we might start to scan them.
+ */
+ for (;;) {
+ packet_reader_read(&reader);
+ if (reader.pktlen <= 0) {
+ break;
+ }
+ }
+
+ /*
+ * v0 smart http; callers expect us to soak up the
+ * service and header packets
+ */
+ d->buf = reader.src_buffer;
+ d->len = reader.src_len;
+ d->proto_git = 1;
+
+ } else if (!strcmp(reader.line, "version 2")) {
+ /*
+ * v2 smart http; do not consume version packet, which will
+ * be handled elsewhere.
+ */
+ d->proto_git = 1;
+
+ } else {
+ die(_("invalid server response; got '%s'"), reader.line);
+ }
+}
+
static struct discovery *discover_refs(const char *service, int for_push)
{
- struct strbuf exp = STRBUF_INIT;
struct strbuf type = STRBUF_INIT;
struct strbuf charset = STRBUF_INIT;
struct strbuf buffer = STRBUF_INIT;
@@ -380,7 +436,6 @@ static struct discovery *discover_refs(const char *service, int for_push)
http_options.extra_headers = &extra_headers;
http_options.initial_request = 1;
http_options.no_cache = 1;
- http_options.keep_error = 1;
http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options);
switch (http_ret) {
@@ -388,55 +443,31 @@ static struct discovery *discover_refs(const char *service, int for_push)
break;
case HTTP_MISSING_TARGET:
show_http_message(&type, &charset, &buffer);
- die("repository '%s' not found", url.buf);
+ die(_("repository '%s' not found"),
+ transport_anonymize_url(url.buf));
case HTTP_NOAUTH:
show_http_message(&type, &charset, &buffer);
- die("Authentication failed for '%s'", url.buf);
+ die(_("Authentication failed for '%s'"),
+ transport_anonymize_url(url.buf));
default:
show_http_message(&type, &charset, &buffer);
- die("unable to access '%s': %s", url.buf, curl_errorstr);
+ die(_("unable to access '%s': %s"),
+ transport_anonymize_url(url.buf), curl_errorstr);
}
- if (options.verbosity && !starts_with(refs_url.buf, url.buf))
- warning(_("redirecting to %s"), url.buf);
+ if (options.verbosity && !starts_with(refs_url.buf, url.buf)) {
+ char *u = transport_anonymize_url(url.buf);
+ warning(_("redirecting to %s"), u);
+ free(u);
+ }
last= xcalloc(1, sizeof(*last_discovery));
last->service = xstrdup(service);
last->buf_alloc = strbuf_detach(&buffer, &last->len);
last->buf = last->buf_alloc;
- strbuf_addf(&exp, "application/x-%s-advertisement", service);
- if (maybe_smart &&
- (5 <= last->len && last->buf[4] == '#') &&
- !strbuf_cmp(&exp, &type)) {
- char *line;
-
- /*
- * smart HTTP response; validate that the service
- * pkt-line matches our request.
- */
- line = packet_read_line_buf(&last->buf, &last->len, NULL);
- if (!line)
- die("invalid server response; expected service, got flush packet");
-
- strbuf_reset(&exp);
- strbuf_addf(&exp, "# service=%s", service);
- if (strcmp(line, exp.buf))
- die("invalid server response; got '%s'", line);
- strbuf_release(&exp);
-
- /* The header can include additional metadata lines, up
- * until a packet flush marker. Ignore these now, but
- * in the future we might start to scan them.
- */
- while (packet_read_line_buf(&last->buf, &last->len, NULL))
- ;
-
- last->proto_git = 1;
- } else if (maybe_smart &&
- last->len > 5 && starts_with(last->buf + 4, "version 2")) {
- last->proto_git = 1;
- }
+ if (maybe_smart)
+ check_smart_http(last, service, &type);
if (last->proto_git)
last->refs = parse_git_refs(last, for_push);
@@ -444,7 +475,6 @@ static struct discovery *discover_refs(const char *service, int for_push)
last->refs = parse_info_refs(last);
strbuf_release(&refs_url);
- strbuf_release(&exp);
strbuf_release(&type);
strbuf_release(&charset);
strbuf_release(&effective_url);
@@ -482,8 +512,6 @@ static void output_refs(struct ref *refs)
struct rpc_state {
const char *service_name;
- const char **argv;
- struct strbuf *stdin_preamble;
char *service_url;
char *hdr_content_type;
char *hdr_accept;
@@ -495,25 +523,121 @@ struct rpc_state {
int in;
int out;
int any_written;
- struct strbuf result;
unsigned gzip_request : 1;
unsigned initial_buffer : 1;
+
+ /*
+ * Whenever a pkt-line is read into buf, append the 4 characters
+ * denoting its length before appending the payload.
+ */
+ unsigned write_line_lengths : 1;
+
+ /*
+ * Used by rpc_out; initialize to 0. This is true if a flush has been
+ * read, but the corresponding line length (if write_line_lengths is
+ * true) and EOF have not been sent to libcurl. Since each flush marks
+ * the end of a request, each flush must be completely sent before any
+ * further reading occurs.
+ */
+ unsigned flush_read_but_not_sent : 1;
};
+/*
+ * Appends the result of reading from rpc->out to the string represented by
+ * rpc->buf and rpc->len if there is enough space. Returns 1 if there was
+ * enough space, 0 otherwise.
+ *
+ * If rpc->write_line_lengths is true, appends the line length as a 4-byte
+ * hexadecimal string before appending the result described above.
+ *
+ * Writes the total number of bytes appended into appended.
+ */
+static int rpc_read_from_out(struct rpc_state *rpc, int options,
+ size_t *appended,
+ enum packet_read_status *status) {
+ size_t left;
+ char *buf;
+ int pktlen_raw;
+
+ if (rpc->write_line_lengths) {
+ left = rpc->alloc - rpc->len - 4;
+ buf = rpc->buf + rpc->len + 4;
+ } else {
+ left = rpc->alloc - rpc->len;
+ buf = rpc->buf + rpc->len;
+ }
+
+ if (left < LARGE_PACKET_MAX)
+ return 0;
+
+ *status = packet_read_with_status(rpc->out, NULL, NULL, buf,
+ left, &pktlen_raw, options);
+ if (*status != PACKET_READ_EOF) {
+ *appended = pktlen_raw + (rpc->write_line_lengths ? 4 : 0);
+ rpc->len += *appended;
+ }
+
+ if (rpc->write_line_lengths) {
+ switch (*status) {
+ case PACKET_READ_EOF:
+ if (!(options & PACKET_READ_GENTLE_ON_EOF))
+ die(_("shouldn't have EOF when not gentle on EOF"));
+ break;
+ case PACKET_READ_NORMAL:
+ set_packet_header(buf - 4, *appended);
+ break;
+ case PACKET_READ_DELIM:
+ memcpy(buf - 4, "0001", 4);
+ break;
+ case PACKET_READ_FLUSH:
+ memcpy(buf - 4, "0000", 4);
+ break;
+ }
+ }
+
+ return 1;
+}
+
static size_t rpc_out(void *ptr, size_t eltsize,
size_t nmemb, void *buffer_)
{
size_t max = eltsize * nmemb;
struct rpc_state *rpc = buffer_;
size_t avail = rpc->len - rpc->pos;
+ enum packet_read_status status;
if (!avail) {
rpc->initial_buffer = 0;
- avail = packet_read(rpc->out, NULL, NULL, rpc->buf, rpc->alloc, 0);
- if (!avail)
- return 0;
+ rpc->len = 0;
rpc->pos = 0;
- rpc->len = avail;
+ if (!rpc->flush_read_but_not_sent) {
+ if (!rpc_read_from_out(rpc, 0, &avail, &status))
+ BUG("The entire rpc->buf should be larger than LARGE_PACKET_MAX");
+ if (status == PACKET_READ_FLUSH)
+ rpc->flush_read_but_not_sent = 1;
+ }
+ /*
+ * If flush_read_but_not_sent is true, we have already read one
+ * full request but have not fully sent it + EOF, which is why
+ * we need to refrain from reading.
+ */
+ }
+ if (rpc->flush_read_but_not_sent) {
+ if (!avail) {
+ /*
+ * The line length either does not need to be sent at
+ * all or has already been completely sent. Now we can
+ * return 0, indicating EOF, meaning that the flush has
+ * been fully sent.
+ */
+ rpc->flush_read_but_not_sent = 0;
+ return 0;
+ }
+ /*
+ * If avail is non-zerp, the line length for the flush still
+ * hasn't been fully sent. Proceed with sending the line
+ * length.
+ */
}
if (max < avail)
@@ -537,7 +661,7 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
rpc->pos = 0;
return CURLIOE_OK;
}
- error("unable to rewind rpc post data - try increasing http.postBuffer");
+ error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
return CURLIOE_FAILRESTART;
default:
@@ -546,14 +670,30 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
}
#endif
+struct rpc_in_data {
+ struct rpc_state *rpc;
+ struct active_request_slot *slot;
+};
+
+/*
+ * A callback for CURLOPT_WRITEFUNCTION. The return value is the bytes consumed
+ * from ptr.
+ */
static size_t rpc_in(char *ptr, size_t eltsize,
size_t nmemb, void *buffer_)
{
size_t size = eltsize * nmemb;
- struct rpc_state *rpc = buffer_;
+ struct rpc_in_data *data = buffer_;
+ long response_code;
+
+ if (curl_easy_getinfo(data->slot->curl, CURLINFO_RESPONSE_CODE,
+ &response_code) != CURLE_OK)
+ return size;
+ if (response_code >= 300)
+ return size;
if (size)
- rpc->any_written = 1;
- write_or_die(rpc->in, ptr, size);
+ data->rpc->any_written = 1;
+ write_or_die(data->rpc->in, ptr, size);
return size;
}
@@ -581,7 +721,7 @@ static int run_slot(struct active_request_slot *slot,
strbuf_addstr(&msg, curl_errorstr);
}
}
- error("RPC failed; %s", msg.buf);
+ error(_("RPC failed; %s"), msg.buf);
strbuf_release(&msg);
}
@@ -617,14 +757,19 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
return err;
}
-static curl_off_t xcurl_off_t(size_t len) {
+static curl_off_t xcurl_off_t(size_t len)
+{
uintmax_t size = len;
if (size > maximum_signed_value_of_type(curl_off_t))
- die("cannot handle pushes this big");
+ die(_("cannot handle pushes this big"));
return (curl_off_t)size;
}
-static int post_rpc(struct rpc_state *rpc)
+/*
+ * If flush_received is true, do not attempt to read any more; just use what's
+ * in rpc->buf.
+ */
+static int post_rpc(struct rpc_state *rpc, int flush_received)
{
struct active_request_slot *slot;
struct curl_slist *headers = http_copy_default_headers();
@@ -633,26 +778,25 @@ static int post_rpc(struct rpc_state *rpc)
size_t gzip_size = 0;
int err, large_request = 0;
int needs_100_continue = 0;
+ struct rpc_in_data rpc_in_data;
/* Try to load the entire request, if we can fit it into the
* allocated buffer space we can use HTTP/1.0 and avoid the
* chunked encoding mess.
*/
- while (1) {
- size_t left = rpc->alloc - rpc->len;
- char *buf = rpc->buf + rpc->len;
- int n;
-
- if (left < LARGE_PACKET_MAX) {
- large_request = 1;
- use_gzip = 0;
- break;
+ if (!flush_received) {
+ while (1) {
+ size_t n;
+ enum packet_read_status status;
+
+ if (!rpc_read_from_out(rpc, 0, &n, &status)) {
+ large_request = 1;
+ use_gzip = 0;
+ break;
+ }
+ if (status == PACKET_READ_FLUSH)
+ break;
}
-
- n = packet_read(rpc->out, NULL, NULL, buf, left, 0);
- if (!n)
- break;
- rpc->len += n;
}
if (large_request) {
@@ -732,11 +876,11 @@ retry:
ret = git_deflate(&stream, Z_FINISH);
if (ret != Z_STREAM_END)
- die("cannot deflate request; zlib deflate error %d", ret);
+ die(_("cannot deflate request; zlib deflate error %d"), ret);
ret = git_deflate_end_gently(&stream);
if (ret != Z_OK)
- die("cannot deflate request; zlib end error %d", ret);
+ die(_("cannot deflate request; zlib end error %d"), ret);
gzip_size = stream.total_out;
@@ -765,7 +909,10 @@ retry:
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
- curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+ rpc_in_data.rpc = rpc;
+ rpc_in_data.slot = slot;
+ curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
+ curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
rpc->any_written = 0;
@@ -785,22 +932,22 @@ retry:
return err;
}
-static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
+static int rpc_service(struct rpc_state *rpc, struct discovery *heads,
+ const char **client_argv, const struct strbuf *preamble,
+ struct strbuf *rpc_result)
{
const char *svc = rpc->service_name;
struct strbuf buf = STRBUF_INIT;
- struct strbuf *preamble = rpc->stdin_preamble;
struct child_process client = CHILD_PROCESS_INIT;
int err = 0;
client.in = -1;
client.out = -1;
client.git_cmd = 1;
- client.argv = rpc->argv;
+ client.argv = client_argv;
if (start_command(&client))
exit(1);
- if (preamble)
- write_or_die(client.in, preamble->buf, preamble->len);
+ write_or_die(client.in, preamble->buf, preamble->len);
if (heads)
write_or_die(client.in, heads->buf, heads->len);
@@ -808,7 +955,6 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
rpc->buf = xmalloc(rpc->alloc);
rpc->in = client.in;
rpc->out = client.out;
- strbuf_init(&rpc->result, 0);
strbuf_addf(&buf, "%s%s", url.buf, svc);
rpc->service_url = strbuf_detach(&buf, NULL);
@@ -830,13 +976,13 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
break;
rpc->pos = 0;
rpc->len = n;
- err |= post_rpc(rpc);
+ err |= post_rpc(rpc, 0);
}
close(client.in);
client.in = -1;
if (!err) {
- strbuf_read(&rpc->result, client.out, 0);
+ strbuf_read(rpc_result, client.out, 0);
} else {
char buf[4096];
for (;;)
@@ -865,7 +1011,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
ALLOC_ARRAY(targets, nr_heads);
if (options.depth || options.deepen_since)
- die("dumb http transport does not support shallow capabilities");
+ die(_("dumb http transport does not support shallow capabilities"));
for (i = 0; i < nr_heads; i++)
targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
@@ -879,7 +1025,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
free(targets[i]);
free(targets);
- return ret ? error("fetch failed.") : 0;
+ return ret ? error(_("fetch failed.")) : 0;
}
static int fetch_git(struct discovery *heads,
@@ -889,6 +1035,7 @@ static int fetch_git(struct discovery *heads,
struct strbuf preamble = STRBUF_INIT;
int i, err;
struct argv_array args = ARGV_ARRAY_INIT;
+ struct strbuf rpc_result = STRBUF_INIT;
argv_array_pushl(&args, "fetch-pack", "--stateless-rpc",
"--stdin", "--lock-pack", NULL);
@@ -926,7 +1073,7 @@ static int fetch_git(struct discovery *heads,
for (i = 0; i < nr_heads; i++) {
struct ref *ref = to_fetch[i];
if (!*ref->name)
- die("cannot fetch by sha1 over smart http");
+ die(_("cannot fetch by sha1 over smart http"));
packet_buf_write(&preamble, "%s %s\n",
oid_to_hex(&ref->old_oid), ref->name);
}
@@ -934,14 +1081,12 @@ static int fetch_git(struct discovery *heads,
memset(&rpc, 0, sizeof(rpc));
rpc.service_name = "git-upload-pack",
- rpc.argv = args.argv;
- rpc.stdin_preamble = &preamble;
rpc.gzip_request = 1;
- err = rpc_service(&rpc, heads);
- if (rpc.result.len)
- write_or_die(1, rpc.result.buf, rpc.result.len);
- strbuf_release(&rpc.result);
+ err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+ if (rpc_result.len)
+ write_or_die(1, rpc_result.buf, rpc_result.len);
+ strbuf_release(&rpc_result);
strbuf_release(&preamble);
argv_array_clear(&args);
return err;
@@ -969,15 +1114,16 @@ static void parse_fetch(struct strbuf *buf)
const char *name;
struct ref *ref;
struct object_id old_oid;
+ const char *q;
- if (get_oid_hex(p, &old_oid))
- die("protocol error: expected sha/ref, got %s'", p);
- if (p[GIT_SHA1_HEXSZ] == ' ')
- name = p + GIT_SHA1_HEXSZ + 1;
- else if (!p[GIT_SHA1_HEXSZ])
+ if (parse_oid_hex(p, &old_oid, &q))
+ die(_("protocol error: expected sha/ref, got %s'"), p);
+ if (*q == ' ')
+ name = q + 1;
+ else if (!*q)
name = "";
else
- die("protocol error: expected sha/ref, got %s'", p);
+ die(_("protocol error: expected sha/ref, got %s'"), p);
ref = alloc_ref(name);
oidcpy(&ref->old_oid, &old_oid);
@@ -989,7 +1135,7 @@ static void parse_fetch(struct strbuf *buf)
to_fetch[nr_heads++] = ref;
}
else
- die("http transport does not support %s", buf->buf);
+ die(_("http transport does not support %s"), buf->buf);
strbuf_reset(buf);
if (strbuf_getline_lf(buf, stdin) == EOF)
@@ -1025,7 +1171,7 @@ static int push_dav(int nr_spec, char **specs)
argv_array_push(&child.args, specs[i]);
if (run_command(&child))
- die("git-http-push failed");
+ die(_("git-http-push failed"));
return 0;
}
@@ -1036,6 +1182,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
struct argv_array args;
struct string_list_item *cas_option;
struct strbuf preamble = STRBUF_INIT;
+ struct strbuf rpc_result = STRBUF_INIT;
argv_array_init(&args);
argv_array_pushl(&args, "send-pack", "--stateless-rpc", "--helper-status",
@@ -1068,13 +1215,11 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
memset(&rpc, 0, sizeof(rpc));
rpc.service_name = "git-receive-pack",
- rpc.argv = args.argv;
- rpc.stdin_preamble = &preamble;
- err = rpc_service(&rpc, heads);
- if (rpc.result.len)
- write_or_die(1, rpc.result.buf, rpc.result.len);
- strbuf_release(&rpc.result);
+ err = rpc_service(&rpc, heads, args.argv, &preamble, &rpc_result);
+ if (rpc_result.len)
+ write_or_die(1, rpc_result.buf, rpc_result.len);
+ strbuf_release(&rpc_result);
strbuf_release(&preamble);
argv_array_clear(&args);
return err;
@@ -1104,7 +1249,7 @@ static void parse_push(struct strbuf *buf)
specs[nr_spec++] = xstrdup(buf->buf + 5);
}
else
- die("http transport does not support %s", buf->buf);
+ die(_("http transport does not support %s"), buf->buf);
strbuf_reset(buf);
if (strbuf_getline_lf(buf, stdin) == EOF)
@@ -1126,164 +1271,11 @@ static void parse_push(struct strbuf *buf)
free(specs);
}
-/*
- * Used to represent the state of a connection to an HTTP server when
- * communicating using git's wire-protocol version 2.
- */
-struct proxy_state {
- char *service_name;
- char *service_url;
- struct curl_slist *headers;
- struct strbuf request_buffer;
- int in;
- int out;
- struct packet_reader reader;
- size_t pos;
- int seen_flush;
-};
-
-static void proxy_state_init(struct proxy_state *p, const char *service_name,
- enum protocol_version version)
-{
- struct strbuf buf = STRBUF_INIT;
-
- memset(p, 0, sizeof(*p));
- p->service_name = xstrdup(service_name);
-
- p->in = 0;
- p->out = 1;
- strbuf_init(&p->request_buffer, 0);
-
- strbuf_addf(&buf, "%s%s", url.buf, p->service_name);
- p->service_url = strbuf_detach(&buf, NULL);
-
- p->headers = http_copy_default_headers();
-
- strbuf_addf(&buf, "Content-Type: application/x-%s-request", p->service_name);
- p->headers = curl_slist_append(p->headers, buf.buf);
- strbuf_reset(&buf);
-
- strbuf_addf(&buf, "Accept: application/x-%s-result", p->service_name);
- p->headers = curl_slist_append(p->headers, buf.buf);
- strbuf_reset(&buf);
-
- p->headers = curl_slist_append(p->headers, "Transfer-Encoding: chunked");
-
- /* Add the Git-Protocol header */
- if (get_protocol_http_header(version, &buf))
- p->headers = curl_slist_append(p->headers, buf.buf);
-
- packet_reader_init(&p->reader, p->in, NULL, 0,
- PACKET_READ_GENTLE_ON_EOF);
-
- strbuf_release(&buf);
-}
-
-static void proxy_state_clear(struct proxy_state *p)
-{
- free(p->service_name);
- free(p->service_url);
- curl_slist_free_all(p->headers);
- strbuf_release(&p->request_buffer);
-}
-
-/*
- * CURLOPT_READFUNCTION callback function.
- * Attempts to copy over a single packet-line at a time into the
- * curl provided buffer.
- */
-static size_t proxy_in(char *buffer, size_t eltsize,
- size_t nmemb, void *userdata)
-{
- size_t max;
- struct proxy_state *p = userdata;
- size_t avail = p->request_buffer.len - p->pos;
-
-
- if (eltsize != 1)
- BUG("curl read callback called with size = %"PRIuMAX" != 1",
- (uintmax_t)eltsize);
- max = nmemb;
-
- if (!avail) {
- if (p->seen_flush) {
- p->seen_flush = 0;
- return 0;
- }
-
- strbuf_reset(&p->request_buffer);
- switch (packet_reader_read(&p->reader)) {
- case PACKET_READ_EOF:
- die("unexpected EOF when reading from parent process");
- case PACKET_READ_NORMAL:
- packet_buf_write_len(&p->request_buffer, p->reader.line,
- p->reader.pktlen);
- break;
- case PACKET_READ_DELIM:
- packet_buf_delim(&p->request_buffer);
- break;
- case PACKET_READ_FLUSH:
- packet_buf_flush(&p->request_buffer);
- p->seen_flush = 1;
- break;
- }
- p->pos = 0;
- avail = p->request_buffer.len;
- }
-
- if (max < avail)
- avail = max;
- memcpy(buffer, p->request_buffer.buf + p->pos, avail);
- p->pos += avail;
- return avail;
-}
-
-static size_t proxy_out(char *buffer, size_t eltsize,
- size_t nmemb, void *userdata)
-{
- size_t size;
- struct proxy_state *p = userdata;
-
- if (eltsize != 1)
- BUG("curl read callback called with size = %"PRIuMAX" != 1",
- (uintmax_t)eltsize);
- size = nmemb;
-
- write_or_die(p->out, buffer, size);
- return size;
-}
-
-/* Issues a request to the HTTP server configured in `p` */
-static int proxy_request(struct proxy_state *p)
-{
- struct active_request_slot *slot;
-
- slot = get_active_slot();
-
- curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
- curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
- curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
- curl_easy_setopt(slot->curl, CURLOPT_URL, p->service_url);
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, p->headers);
-
- /* Setup function to read request from client */
- curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, proxy_in);
- curl_easy_setopt(slot->curl, CURLOPT_READDATA, p);
-
- /* Setup function to write server response to client */
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, proxy_out);
- curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, p);
-
- if (run_slot(slot, NULL) != HTTP_OK)
- return -1;
-
- return 0;
-}
-
static int stateless_connect(const char *service_name)
{
struct discovery *discover;
- struct proxy_state p;
+ struct rpc_state rpc;
+ struct strbuf buf = STRBUF_INIT;
/*
* Run the info/refs request and see if the server supports protocol
@@ -1303,23 +1295,58 @@ static int stateless_connect(const char *service_name)
fflush(stdout);
}
- proxy_state_init(&p, service_name, discover->version);
+ rpc.service_name = service_name;
+ rpc.service_url = xstrfmt("%s%s", url.buf, rpc.service_name);
+ rpc.hdr_content_type = xstrfmt("Content-Type: application/x-%s-request", rpc.service_name);
+ rpc.hdr_accept = xstrfmt("Accept: application/x-%s-result", rpc.service_name);
+ if (get_protocol_http_header(discover->version, &buf)) {
+ rpc.protocol_header = strbuf_detach(&buf, NULL);
+ } else {
+ rpc.protocol_header = NULL;
+ strbuf_release(&buf);
+ }
+ rpc.buf = xmalloc(http_post_buffer);
+ rpc.alloc = http_post_buffer;
+ rpc.len = 0;
+ rpc.pos = 0;
+ rpc.in = 1;
+ rpc.out = 0;
+ rpc.any_written = 0;
+ rpc.gzip_request = 1;
+ rpc.initial_buffer = 0;
+ rpc.write_line_lengths = 1;
+ rpc.flush_read_but_not_sent = 0;
/*
* Dump the capability listing that we got from the server earlier
* during the info/refs request.
*/
- write_or_die(p.out, discover->buf, discover->len);
+ write_or_die(rpc.in, discover->buf, discover->len);
+
+ /* Until we see EOF keep sending POSTs */
+ while (1) {
+ size_t avail;
+ enum packet_read_status status;
- /* Peek the next packet line. Until we see EOF keep sending POSTs */
- while (packet_reader_peek(&p.reader) != PACKET_READ_EOF) {
- if (proxy_request(&p)) {
+ if (!rpc_read_from_out(&rpc, PACKET_READ_GENTLE_ON_EOF, &avail,
+ &status))
+ BUG("The entire rpc->buf should be larger than LARGE_PACKET_MAX");
+ if (status == PACKET_READ_EOF)
+ break;
+ if (post_rpc(&rpc, status == PACKET_READ_FLUSH))
/* We would have an err here */
break;
- }
+ /* Reset the buffer for next request */
+ rpc.len = 0;
}
- proxy_state_clear(&p);
+ free(rpc.service_url);
+ free(rpc.hdr_content_type);
+ free(rpc.hdr_accept);
+ free(rpc.protocol_header);
+ free(rpc.buf);
+ strbuf_release(&buf);
+
return 0;
}
@@ -1330,7 +1357,7 @@ int cmd_main(int argc, const char **argv)
setup_git_directory_gently(&nongit);
if (argc < 2) {
- error("remote-curl: usage: git remote-curl <remote> [<url>]");
+ error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
return 1;
}
@@ -1340,6 +1367,13 @@ int cmd_main(int argc, const char **argv)
string_list_init(&options.deepen_not, 1);
string_list_init(&options.push_options, 1);
+ /*
+ * Just report "remote-curl" here (folding all the various aliases
+ * ("git-remote-http", "git-remote-https", and etc.) here since they
+ * are all just copies of the same actual executable.
+ */
+ trace2_cmd_name("remote-curl");
+
remote = remote_get(argv[1]);
if (argc > 2) {
@@ -1355,14 +1389,14 @@ int cmd_main(int argc, const char **argv)
if (strbuf_getline_lf(&buf, stdin) == EOF) {
if (ferror(stdin))
- error("remote-curl: error reading command stream from git");
+ error(_("remote-curl: error reading command stream from git"));
return 1;
}
if (buf.len == 0)
break;
if (starts_with(buf.buf, "fetch ")) {
if (nongit)
- die("remote-curl: fetch attempted without a local repo");
+ die(_("remote-curl: fetch attempted without a local repo"));
parse_fetch(&buf);
} else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
@@ -1402,7 +1436,7 @@ int cmd_main(int argc, const char **argv)
if (!stateless_connect(arg))
break;
} else {
- error("remote-curl: unknown command '%s' from git", buf.buf);
+ error(_("remote-curl: unknown command '%s' from git"), buf.buf);
return 1;
}
strbuf_reset(&buf);