-
Notifications
You must be signed in to change notification settings - Fork 32
feat: http debug transport #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
47b7cf8
d962469
76d2e96
bf721e8
14600d8
915abe9
a384455
a5131dc
935fed3
daaab33
0ded7cf
b4e165f
5c452ef
1728d06
a72bbfe
f7b2f6b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,70 @@ | ||
| .PHONY: modernize modernize-fix modernize-check | ||
| #---------------------- | ||
| # Parse makefile arguments | ||
| #---------------------- | ||
| RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) | ||
| $(eval $(RUN_ARGS):;@:) | ||
|
|
||
| MODERNIZE_CMD = go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/[email protected] | ||
| #---------------------- | ||
| # Silence GNU Make | ||
| #---------------------- | ||
| ifndef VERBOSE | ||
| MAKEFLAGS += --no-print-directory | ||
| endif | ||
|
|
||
| #---------------------- | ||
| # Terminal | ||
| #---------------------- | ||
|
|
||
| GREEN := $(shell tput -Txterm setaf 2) | ||
| WHITE := $(shell tput -Txterm setaf 7) | ||
| YELLOW := $(shell tput -Txterm setaf 3) | ||
| RESET := $(shell tput -Txterm sgr0) | ||
|
|
||
| #------------------------------------------------------------------ | ||
| # - Add the following 'help' target to your Makefile | ||
| # - Add help text after each target name starting with '\#\#' | ||
| # - A category can be added with @category | ||
| #------------------------------------------------------------------ | ||
|
|
||
| HELP_FUN = \ | ||
| %help; \ | ||
| while(<>) { \ | ||
| push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \ | ||
| print "\n"; \ | ||
| for (sort keys %help) { \ | ||
| print "${WHITE}$$_${RESET \ | ||
| }\n"; \ | ||
| for (@{$$help{$$_}}) { \ | ||
| $$sep = " " x (32 - length $$_->[0]); \ | ||
| print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \ | ||
| }; \ | ||
| print ""; \ | ||
| } | ||
|
|
||
| modernize: modernize-fix | ||
| help: ##@other Show this help. | ||
| @perl -e '$(HELP_FUN)' $(MAKEFILE_LIST) | ||
|
|
||
| modernize-fix: | ||
| #---------------------- | ||
| # tool | ||
| #---------------------- | ||
|
|
||
| MODERNIZE_CMD = go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/[email protected] | ||
|
|
||
| modernize-fix: ##@tool Run modernize fix | ||
| @echo "Running gopls modernize with -fix..." | ||
| $(MODERNIZE_CMD) -test -fix ./... | ||
|
|
||
| modernize-check: | ||
| modernize-check: ##@tool Run modernize check | ||
| @echo "Checking if code needs modernization..." | ||
| $(MODERNIZE_CMD) -test ./... | ||
|
|
||
| linter-run: ##@tool Run Go linter | ||
| @echo "Running linter..." | ||
| go run github.com/golangci/golangci-lint/v2/cmd/[email protected] run -v | ||
| @echo "Linter run complete." | ||
|
|
||
| run-all: ##@tool Run all tools | ||
| @echo "Running all tools..." | ||
| make modernize-check | ||
| make linter-run | ||
| @echo "All tools run complete." |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,141 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package godump | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "fmt" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "net/http" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "net/http/httputil" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "os" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sort" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "strings" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // HTTPDebugTransport wraps a http.RoundTripper to optionally log requests and responses. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type HTTPDebugTransport struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Transport http.RoundTripper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| debugEnabled bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dumper *Dumper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // NewHTTPDebugTransport creates a HTTPDebugTransport with debug flag cached from env. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func NewHTTPDebugTransport(inner http.RoundTripper, opts ...Option) *HTTPDebugTransport { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| combinedOpts := append([]Option{WithSkipStackFrames(4)}, opts...) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return &HTTPDebugTransport{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Transport: inner, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| debugEnabled: os.Getenv("HTTP_DEBUG") != "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dumper: NewDumper(combinedOpts...), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // SetDebug allows toggling debug logging at runtime. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (t *HTTPDebugTransport) SetDebug(enabled bool) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.debugEnabled = enabled | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dumper returns the Dumper instance used for logging. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (t *HTTPDebugTransport) Dumper() *Dumper { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return t.dumper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // RoundTrip implements the http.RoundTripper interface. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func (t *HTTPDebugTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !t.debugEnabled { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resp, err := t.Transport.RoundTrip(req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return resp, fmt.Errorf("HTTPDebugTransport: pass-through round trip failed: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return resp, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start := time.Now() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dump Request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reqDump, err := httputil.DumpRequestOut(req, true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("HTTPDebugTransport: failed to dump request: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request := parseHTTPDump("Request", string(reqDump)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Perform request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resp, err := t.Transport.RoundTrip(req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("HTTPDebugTransport: round trip failed: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration := time.Since(start) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dump Response | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| resDump, err := httputil.DumpResponse(resp, true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("HTTPDebugTransport: failed to dump response: %w", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response := parseHTTPDump("Response", string(resDump)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Combine and dump | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| transaction := map[string]any{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Transaction": map[string]any{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Request": request, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Response": response, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Duration": duration.String(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.dumper.Dump(transaction) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return resp, nil | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // parseHTTPDump parses the raw HTTP dump into a structured map. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func parseHTTPDump(label, raw string) map[string]any { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| lines := strings.Split(raw, "\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload := make(map[string]any) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers := make(map[string]string) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inBody := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var bodyBuilder strings.Builder | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for i, line := range lines { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| line = strings.TrimRight(line, "\r\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Skip empty lines | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if i == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if label == "Request" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload["Request-Line"] = line | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload["Status"] = line | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If we are in the body, accumulate lines | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if inBody { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bodyBuilder.WriteString(line + "\n") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If we hit an empty line, switch to body mode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if line == "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inBody = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse headers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if key, value, found := strings.Cut(line, ":"); found { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| headers[strings.TrimSpace(key)] = strings.TrimSpace(value) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Alphabetize headers | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys := make([]string, 0, len(headers)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for k := range headers { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| keys = append(keys, k) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sort.Strings(keys) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sort.Strings is somehow deprecated
use slices.Sort
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for _, k := range keys { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload[k] = headers[k] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add body as raw | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body := strings.TrimSpace(bodyBuilder.String()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if body != "" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| payload["Body"] = body | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+134
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here I would expect the body to be decoded as JSON in
Suggested change
Or maybe this
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code that is here doesn't make any sense. The last main remaining action item of this PR is to address body printing which I'll do at a later point. I'm not in a rush to merge this. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return payload | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.