http.c: implement the GIT_TRACE_CURL environment variable

Implement the GIT_TRACE_CURL environment variable to allow a greater degree of detail of GIT_CURL_VERBOSE, in particular the complete transport header and all the data payload exchanged. It might be useful if a particular situation could require a more thorough debugging analysis. Document the new GIT_TRACE_CURL environment variable. Helped-by: Torsten Bögershausen <tboegi@web.de> Helped-by: Ramsay Jones <ramsay@ramsayjones.plus.com> Helped-by: Junio C Hamano <gitster@pobox.com> Helped-by: Eric Sunshine <sunshine@sunshineco.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Elia Pinto <gitter.spiros@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elia Pinto committed May 23, 2016 at 13:44 UTC 74c682d3c63bff8860af5caf9bd38a5413568709
3 files changed +132 -2
Documentation/git.txt
+8
@@ -1075,6 +1075,14 @@ of clones and fetches.
1075 cloning of shallow repositories.
1076 See 'GIT_TRACE' for available trace output options.
1077
1078 +'GIT_TRACE_CURL'::
1079 + Enables a curl full trace dump of all incoming and outgoing data,
1080 + including descriptive information, of the git transport protocol.
1081 + This is similar to doing curl --trace-ascii on the command line.
1082 + This option overrides setting the GIT_CURL_VERBOSE environment
1083 + variable.
1084 + See 'GIT_TRACE' for available trace output options.
1085 +
1086 'GIT_LITERAL_PATHSPECS'::
1087 Setting this variable to `1` will cause Git to treat all
1088 pathspecs literally, rather than as glob patterns. For example,
http.c
+122 -2
@@ -11,6 +11,7 @@
11 #include "gettext.h"
12 #include "transport.h"
13
14 +static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
15 #if LIBCURL_VERSION_NUM >= 0x070a08
16 long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
17 #else
@@ -464,6 +465,125 @@ static void set_curl_keepalive(CURL *c)
465 }
466 #endif
467
468 +static void redact_sensitive_header(struct strbuf *header)
469 +{
470 + const char *sensitive_header;
471 +
472 + if (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
473 + skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) {
474 + /* The first token is the type, which is OK to log */
475 + while (isspace(*sensitive_header))
476 + sensitive_header++;
477 + while (*sensitive_header && !isspace(*sensitive_header))
478 + sensitive_header++;
479 + /* Everything else is opaque and possibly sensitive */
480 + strbuf_setlen(header, sensitive_header - header->buf);
481 + strbuf_addstr(header, " <redacted>");
482 + }
483 +}
484 +
485 +static void curl_dump_header(const char *text, unsigned char *ptr, size_t size, int hide_sensitive_header)
486 +{
487 + struct strbuf out = STRBUF_INIT;
488 + struct strbuf **headers, **header;
489 +
490 + strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
491 + text, (long)size, (long)size);
492 + trace_strbuf(&trace_curl, &out);
493 + strbuf_reset(&out);
494 + strbuf_add(&out, ptr, size);
495 + headers = strbuf_split_max(&out, '\n', 0);
496 +
497 + for (header = headers; *header; header++) {
498 + if (hide_sensitive_header)
499 + redact_sensitive_header(*header);
500 + strbuf_insert((*header), 0, text, strlen(text));
501 + strbuf_insert((*header), strlen(text), ": ", 2);
502 + strbuf_rtrim((*header));
503 + strbuf_addch((*header), '\n');
504 + trace_strbuf(&trace_curl, (*header));
505 + }
506 + strbuf_list_free(headers);
507 + strbuf_release(&out);
508 +}
509 +
510 +static void curl_dump_data(const char *text, unsigned char *ptr, size_t size)
511 +{
512 + size_t i;
513 + struct strbuf out = STRBUF_INIT;
514 + unsigned int width = 60;
515 +
516 + strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
517 + text, (long)size, (long)size);
518 + trace_strbuf(&trace_curl, &out);
519 +
520 + for (i = 0; i < size; i += width) {
521 + size_t w;
522 +
523 + strbuf_reset(&out);
524 + strbuf_addf(&out, "%s: ", text);
525 + for (w = 0; (w < width) && (i + w < size); w++) {
526 + unsigned char ch = ptr[i + w];
527 +
528 + strbuf_addch(&out,
529 + (ch >= 0x20) && (ch < 0x80)
530 + ? ch : '.');
531 + }
532 + strbuf_addch(&out, '\n');
533 + trace_strbuf(&trace_curl, &out);
534 + }
535 + strbuf_release(&out);
536 +}
537 +
538 +static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
539 +{
540 + const char *text;
541 + enum { NO_FILTER = 0, DO_FILTER = 1 };
542 +
543 + switch (type) {
544 + case CURLINFO_TEXT:
545 + trace_printf_key(&trace_curl, "== Info: %s", data);
546 + default: /* we ignore unknown types by default */
547 + return 0;
548 +
549 + case CURLINFO_HEADER_OUT:
550 + text = "=> Send header";
551 + curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
552 + break;
553 + case CURLINFO_DATA_OUT:
554 + text = "=> Send data";
555 + curl_dump_data(text, (unsigned char *)data, size);
556 + break;
557 + case CURLINFO_SSL_DATA_OUT:
558 + text = "=> Send SSL data";
559 + curl_dump_data(text, (unsigned char *)data, size);
560 + break;
561 + case CURLINFO_HEADER_IN:
562 + text = "<= Recv header";
563 + curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);
564 + break;
565 + case CURLINFO_DATA_IN:
566 + text = "<= Recv data";
567 + curl_dump_data(text, (unsigned char *)data, size);
568 + break;
569 + case CURLINFO_SSL_DATA_IN:
570 + text = "<= Recv SSL data";
571 + curl_dump_data(text, (unsigned char *)data, size);
572 + break;
573 + }
574 + return 0;
575 +}
576 +
577 +void setup_curl_trace(CURL *handle)
578 +{
579 + if (!trace_want(&trace_curl))
580 + return;
581 + curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
582 + curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curl_trace);
583 + curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
584 +}
585 +
586 +
587 static CURL *get_curl_handle(void)
588 {
589 CURL *result = curl_easy_init();
@@ -562,9 +682,9 @@ static CURL *get_curl_handle(void)
682 warning("protocol restrictions not applied to curl redirects because\n"
683 "your curl version is too old (>= 7.19.4)");
684 #endif
565 -
685 if (getenv("GIT_CURL_VERBOSE"))
567 - curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
686 + curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
687 + setup_curl_trace(result);
688
689 curl_easy_setopt(result, CURLOPT_USERAGENT,
690 user_agent ? user_agent : git_user_agent());
http.h
+2
@@ -224,4 +224,6 @@ extern int finish_http_object_request(struct http_object_request *freq);
224 extern void abort_http_object_request(struct http_object_request *freq);
225 extern void release_http_object_request(struct http_object_request *freq);
226
227 +/* setup routine for curl_easy_setopt CURLOPT_DEBUGFUNCTION */
228 +void setup_curl_trace(CURL *handle);
229 #endif /* HTTP_H */