Merge branch 'js/anonymize-remote-curl-diag'
authorJunio C Hamano <gitster@pobox.com>
Tue, 16 Apr 2019 10:28:04 +0000 (19:28 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 16 Apr 2019 10:28:04 +0000 (19:28 +0900)
remote-http transport did not anonymize URLs reported in its error
messages at places.

* js/anonymize-remote-curl-diag:
curl: anonymize URLs in error messages and warnings

1  2 
remote-curl.c
diff --combined remote-curl.c
index 5b447949228cbf853f35ff9c3cccd4ea7a0f8afc,6be3cb59184067c76d9efe1ecca84770ddc8d225..78f340f004e36bdfd84687f1ee803254b8fb8d43
@@@ -16,6 -16,7 +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 */
@@@ -204,8 -205,7 +205,8 @@@ static struct ref *parse_git_refs(struc
  
        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) {
@@@ -251,7 -251,7 +252,7 @@@ static struct ref *parse_info_refs(stru
                if (data[i] == '\n') {
                        if (mid - start != 40)
                                die("%sinfo/refs not valid: is this a git repository?",
-                                   url.buf);
+                                   transport_anonymize_url(url.buf));
                        data[i] = 0;
                        ref_name = mid + 1;
                        ref = alloc_ref(ref_name);
@@@ -331,63 -331,9 +332,63 @@@ static int get_protocol_http_header(enu
        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;
        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) {
                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);
                last->refs = parse_info_refs(last);
  
        strbuf_release(&refs_url);
 -      strbuf_release(&exp);
        strbuf_release(&type);
        strbuf_release(&charset);
        strbuf_release(&effective_url);
@@@ -505,6 -489,8 +512,6 @@@ static void output_refs(struct ref *ref
  
  struct rpc_state {
        const char *service_name;
 -      const char **argv;
 -      struct strbuf *stdin_preamble;
        char *service_url;
        char *hdr_content_type;
        char *hdr_accept;
        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)
@@@ -663,30 -553,14 +670,30 @@@ static curlioerr rpc_ioctl(CURL *handle
  }
  #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;
  }
  
@@@ -750,19 -624,13 +757,19 @@@ static int probe_rpc(struct rpc_state *
        return err;
  }
  
 -static curl_off_t xcurl_off_t(ssize_t len) {
 -      if (len > maximum_signed_value_of_type(curl_off_t))
 +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");
 -      return (curl_off_t) len;
 +      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();
        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) {
@@@ -902,10 -771,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;
        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);
  
        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);
                        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 (;;)
@@@ -1028,7 -895,6 +1035,7 @@@ static int fetch_git(struct discovery *
        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);
  
        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;
@@@ -1174,7 -1042,6 +1181,7 @@@ static int push_git(struct discovery *h
        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",
  
        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;
@@@ -1263,11 -1132,164 +1270,11 @@@ static void parse_push(struct strbuf *b
        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
                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;
  }
  
@@@ -1359,13 -1346,6 +1366,13 @@@ int cmd_main(int argc, const char **arg
        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) {