-
Notifications
You must be signed in to change notification settings - Fork 25
Closed
Labels
Type: BugInconsistencies or issues which will cause an issue or problem for users or implementors.Inconsistencies or issues which will cause an issue or problem for users or implementors.
Description
retryablehttp version:
v1.0.133
Current Behavior:
When sending POST requests with a non-empty body (e.g., multipart/form-data) that trigger 307/308 redirects via retryablehttp-go (especially with DefaultOptionsSpraying), the redirects fail.
Root cause: The underlying http.Request.GetBody is nil by default (not auto-populated by the library), and Go’s net/http blocks 307/308 redirects for POST requests with non-empty bodies if GetBody is unimplemented。
Steps To Reproduce:
I have written the test code as follows
package main
import (
"bytes"
"context"
"fmt"
"net/http"
"strings"
"github.com/projectdiscovery/retryablehttp-go"
)
var (
url = "http://127.0.0.1:8080/redirect"
boundary = "----WebKitFormBoundaryx8jO2oVc6SWP3Sad"
)
func body() *bytes.Buffer {
var formBuilder strings.Builder
formBuilder.WriteString(fmt.Sprintf("--%s\r\n", boundary))
formBuilder.WriteString(`Content-Disposition: form-data; name="1"` + "\r\n\r\n")
formBuilder.WriteString(`"$@0"` + "\r\n")
formBuilder.WriteString(fmt.Sprintf("--%s--\r\n", boundary))
return bytes.NewBufferString(formBuilder.String())
}
func normalHttp() {
req, err := http.NewRequest("POST", url, body())
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary)
req.Header.Set("Host", "127.0.0.1:8080")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
defer resp.Body.Close()
fmt.Printf("code: %d\n", resp.StatusCode)
fmt.Printf("currentUrl:%s\noriginUrl:%s\n", resp.Request.URL.String(), req.URL.String())
}
func retryHttp() {
opts := retryablehttp.DefaultOptionsSpraying
httpclient := retryablehttp.NewClient(opts)
req, _ := retryablehttp.NewRequestWithContext(context.Background(), "POST", url, body())
//req.GetBody = func() (io.ReadCloser, error) {
// return io.NopCloser(body()), nil
//} // issue NOT set req.GetBody
req.Header.Set("Content-Type", "multipart/form-data; boundary="+boundary)
req.Header.Set("Host", "127.0.0.1:8080")
resp, err := httpclient.Do(req)
if err != nil {
fmt.Printf("err: %v\n", err)
return
}
defer resp.Body.Close()
fmt.Printf("code: %d\n", resp.StatusCode)
fmt.Printf("currentUrl:%s\noriginUrl:%s\n", resp.Request.URL.String(), req.URL.String())
}
func main() {
fmt.Println("-----------http.Client-----------")
normalHttp()
fmt.Println("-----------retryablehttp-----------")
retryHttp()
}Redirect service for testing
package main
import (
"fmt"
"io"
"log"
"net/http"
)
const (
ListenAddr = ":8080"
RedirectPath = "/redirect"
TargetPath = "/target"
TargetHost = "http://127.0.0.1:8080"
)
func redirectHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "only post:%s\n", r.Method)
return
}
log.Printf("[307 redirect] path:%s", r.URL.Path)
_, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("err:%v", err)
}
defer r.Body.Close()
w.Header().Set("Location", TargetHost+TargetPath)
w.WriteHeader(http.StatusTemporaryRedirect)
fmt.Fprintf(w, "redirect to target:%s%s\n", TargetHost, TargetPath)
}
func targetHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[307 target] path:%s ", r.URL.Path)
_, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("err:%v", err)
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(w, "err:%v\n", err)
return
}
defer r.Body.Close()
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "307 success\n")
}
func main() {
http.HandleFunc(RedirectPath, redirectHandler) // 307 redirect
http.HandleFunc(TargetPath, targetHandler) // 307 target
log.Printf("307 redirect:%s%s", TargetHost, RedirectPath)
log.Printf("307 target:%s%s", TargetHost, TargetPath)
if err := http.ListenAndServe(ListenAddr, nil); err != nil {
log.Fatalf("err:%v", err)
}
}This library (retryablehttp-go) may have an impact on Nuclei.
Anything else:
Metadata
Metadata
Assignees
Labels
Type: BugInconsistencies or issues which will cause an issue or problem for users or implementors.Inconsistencies or issues which will cause an issue or problem for users or implementors.