(method, uri string, headers []header, body string, proxy *url.URL, timeout int, redirect bool)
| 184 | } |
| 185 | |
| 186 | func requestBody(method, uri string, headers []header, body string, proxy *url.URL, timeout int, redirect bool) (ResponseInfo, error) { |
| 187 | if method == "" { |
| 188 | method = "GET" |
| 189 | } |
| 190 | |
| 191 | if proxy == nil || len(proxy.Host) == 0 { |
| 192 | proxy = nil |
| 193 | } |
| 194 | |
| 195 | // net/http and url.Parse do not accept legacy IIS-style %uXXXX escapes. |
| 196 | // Route those requests through the raw client so the request target is sent as-is. |
| 197 | if strings.Contains(strings.ToLower(uri), "%u") && proxy == nil && !redirect { |
| 198 | return rawRequest(method, uri, rawRequestTarget(uri), headers, body, timeout) |
| 199 | } |
| 200 | |
| 201 | client := getClient(proxy, timeout, redirect) |
| 202 | |
| 203 | parsedURL, err := url.Parse(uri) |
| 204 | if err != nil || parsedURL == nil || parsedURL.Scheme == "" || parsedURL.Host == "" { |
| 205 | // Fallback for non-standard encoding (e.g., %u002f unicode escapes) |
| 206 | // that url.Parse rejects. Extract scheme/host manually and preserve |
| 207 | // the raw path so the server receives it as-is. |
| 208 | parsedURL, err = parseRawURL(uri) |
| 209 | if err != nil { |
| 210 | return ResponseInfo{}, fmt.Errorf("invalid URL: %q", uri) |
| 211 | } |
| 212 | } else { |
| 213 | parsedURL.RawPath = parsedURL.EscapedPath() |
| 214 | } |
| 215 | |
| 216 | req, err := http.NewRequest(method, parsedURL.String(), strings.NewReader(body)) |
| 217 | if err != nil { |
| 218 | return ResponseInfo{}, err |
| 219 | } |
| 220 | req.Host = parsedURL.Host |
| 221 | req.URL = parsedURL |
| 222 | req.Header = make(http.Header) |
| 223 | |
| 224 | for _, header := range headers { |
| 225 | // Go's net/http ignores req.Header["Host"] — it uses req.Host instead. |
| 226 | // Set req.Host directly so Host header variations are actually sent. |
| 227 | if strings.EqualFold(header.key, "Host") { |
| 228 | req.Host = header.value |
| 229 | } else { |
| 230 | req.Header.Add(header.key, header.value) |
| 231 | } |
| 232 | } |
| 233 | if body != "" && req.Header.Get("Content-Length") == "" { |
| 234 | req.Header.Set("Content-Length", strconv.Itoa(len(body))) |
| 235 | } |
| 236 | |
| 237 | res, err := client.Do(req) |
| 238 | if err != nil { |
| 239 | return ResponseInfo{}, err |
| 240 | } |
| 241 | defer func() { |
| 242 | if cerr := res.Body.Close(); cerr != nil { |
| 243 | log.Printf("[!] Error closing response body: %v", cerr) |
no test coverage detected