diff options
Diffstat (limited to 'remote-curl.c')
-rw-r--r-- | remote-curl.c | 584 |
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); |