-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsender.go
More file actions
122 lines (102 loc) · 3.08 KB
/
Copy pathsender.go
File metadata and controls
122 lines (102 loc) · 3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
import (
"compress/flate"
"compress/gzip"
"crypto/tls"
"fmt"
"io"
"net/http"
"strings"
"golang.org/x/net/http2"
)
// h2Transport is a reusable HTTP/2-only transport.
// It negotiates TLS with ALPN "h2" and speaks HTTP/2 directly.
var h2Transport = &http2.Transport{
TLSClientConfig: &tls.Config{
NextProtos: []string{"h2"},
},
}
// h1Transport is the default HTTP/1.1 transport.
var h1Transport = http.DefaultTransport
func SendRequest(req ParsedRequest) (int, http.Header, string, error) {
fullURL := req.Scheme + "://" + req.Host + req.Path
httpReq, err := http.NewRequest(req.Method, fullURL, strings.NewReader(req.Body))
if err != nil {
return 0, nil, "", fmt.Errorf("building request: %w", err)
}
for k, vals := range req.Headers {
for _, v := range vals {
httpReq.Header.Set(k, v)
}
}
// Strip brotli from Accept-Encoding since we don't have a decoder.
// Keep gzip and deflate which we can decompress natively.
sanitizeAcceptEncoding(httpReq)
// Pick transport based on the original protocol version.
var transport http.RoundTripper
if req.Proto == "HTTP/2" {
transport = h2Transport
} else {
transport = h1Transport
}
client := &http.Client{Transport: transport}
resp, err := client.Do(httpReq)
if err != nil {
return 0, nil, "", fmt.Errorf("sending request: %w", err)
}
defer resp.Body.Close()
// Decompress the response body based on Content-Encoding.
// http2.Transport does not auto-decompress like http.Transport,
// and Burp exports typically include Accept-Encoding headers
// that cause the server to send compressed responses.
body, err := decompressBody(resp)
if err != nil {
return 0, nil, "", fmt.Errorf("reading response: %w", err)
}
return resp.StatusCode, resp.Header, string(body), nil
}
// decompressBody reads the response body and decompresses it
// based on the Content-Encoding header (gzip, deflate).
func decompressBody(resp *http.Response) ([]byte, error) {
encoding := strings.ToLower(resp.Header.Get("Content-Encoding"))
var reader io.ReadCloser
var err error
switch encoding {
case "gzip":
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("gzip decoder: %w", err)
}
defer reader.Close()
case "deflate":
reader = flate.NewReader(resp.Body)
defer reader.Close()
default:
reader = resp.Body
}
return io.ReadAll(reader)
}
// sanitizeAcceptEncoding removes encodings we can't decompress (brotli)
// from the Accept-Encoding header so the server sends a format we handle.
func sanitizeAcceptEncoding(req *http.Request) {
ae := req.Header.Get("Accept-Encoding")
if ae == "" {
return
}
var supported []string
for _, enc := range strings.Split(ae, ",") {
enc = strings.TrimSpace(enc)
// Keep gzip, deflate, identity — drop br (brotli) and zstd.
name := strings.SplitN(enc, ";", 2)[0]
name = strings.TrimSpace(strings.ToLower(name))
if name == "br" || name == "zstd" {
continue
}
supported = append(supported, enc)
}
if len(supported) == 0 {
req.Header.Del("Accept-Encoding")
} else {
req.Header.Set("Accept-Encoding", strings.Join(supported, ", "))
}
}