diff --git a/api/client.go b/api/client.go index 2aa82d1ea..040527053 100644 --- a/api/client.go +++ b/api/client.go @@ -7,6 +7,8 @@ import ( "io" "io/ioutil" "net/http" + "regexp" + "strings" ) // ClientOption represents an argument to NewClient @@ -34,13 +36,26 @@ func AddHeader(name, value string) ClientOption { } // VerboseLog enables request/response logging within a RoundTripper -func VerboseLog(out io.Writer) ClientOption { +func VerboseLog(out io.Writer, logBodies bool) ClientOption { return func(tr http.RoundTripper) http.RoundTripper { return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) { fmt.Fprintf(out, "> %s %s\n", req.Method, req.URL.RequestURI()) + if logBodies && req.Body != nil && inspectableMIMEType(req.Header.Get("Content-type")) { + newBody := &bytes.Buffer{} + io.Copy(out, io.TeeReader(req.Body, newBody)) + fmt.Fprintln(out) + req.Body = ioutil.NopCloser(newBody) + } res, err := tr.RoundTrip(req) if err == nil { fmt.Fprintf(out, "< HTTP %s\n", res.Status) + if logBodies && res.Body != nil && inspectableMIMEType(res.Header.Get("Content-type")) { + newBody := &bytes.Buffer{} + // TODO: pretty-print response JSON + io.Copy(out, io.TeeReader(res.Body, newBody)) + fmt.Fprintln(out) + res.Body = ioutil.NopCloser(newBody) + } } return res, err }} @@ -179,3 +194,9 @@ func handleHTTPError(resp *http.Response) error { return fmt.Errorf("http error, '%s' failed (%d): '%s'", resp.Request.URL, resp.StatusCode, message) } + +var jsonTypeRE = regexp.MustCompile(`[/+]json($|;)`) + +func inspectableMIMEType(t string) bool { + return strings.HasPrefix(t, "text/") || jsonTypeRE.MatchString(t) +} diff --git a/command/root.go b/command/root.go index 5703f30c2..e443d391e 100644 --- a/command/root.go +++ b/command/root.go @@ -84,7 +84,7 @@ func BasicClient() (*api.Client, error) { opts = append(opts, api.AddHeader("Authorization", fmt.Sprintf("token %s", c.Token))) } if verbose := os.Getenv("DEBUG"); verbose != "" { - opts = append(opts, api.VerboseLog(os.Stderr)) + opts = append(opts, api.VerboseLog(os.Stderr, false)) } return api.NewClient(opts...), nil } @@ -113,7 +113,7 @@ var apiClientForContext = func(ctx context.Context) (*api.Client, error) { api.AddHeader("GraphQL-Features", "pe_mobile"), } if verbose := os.Getenv("DEBUG"); verbose != "" { - opts = append(opts, api.VerboseLog(os.Stderr)) + opts = append(opts, api.VerboseLog(os.Stderr, strings.Contains(verbose, "api"))) } return api.NewClient(opts...), nil }