-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Summary
When using fasthttp.Client for many different targets, I would like to be able to override the HTTP Host header (similar to net/http's Request.Host) without changing the dial target / causing DNS resolution for that Host value.
Right now, using SetHost / Header.SetHost appears to influence what address is passed to Client.Dial, so if Host is a domain name it can trigger DNS resolution even if the request URI contains a literal IP. This makes it hard to connect by IP but still send a different Host header.
I have searched existing issues, including #904, #841 and several related discussions, but they do not resolve this specific requirement.
Environment
- fasthttp version: latest v1.x (via
github.com/valyala/fasthttp) - Go version: 1.22.x
- Platform: Linux
Use case
I am building a scanner that sends requests to many different IPs, but for each request I sometimes need to spoof the HTTP Host header (for example: virtual hosts, CDN/WAF frontends, etc.).
Key requirements:
- Dial target must be the IP from the URL (no DNS for the Host header)
- I do not want to create a separate
HostClientfor every possible host string (there can be a lot) - I want behavior similar to
net/httpwhere:- URL controls where we dial (IP/hostname)
req.Hostonly overrides the HTTPHostheader
Minimal reproduction
package main
import (
"errors"
"fmt"
"net"
"time"
"github.com/valyala/fasthttp"
)
func main() {
var dialAddrs []string
client := &fasthttp.Client{
Dial: func(addr string) (net.Conn, error) {
dialAddrs = append(dialAddrs, addr)
// stop before real network I/O
return nil, errors.New("stop")
},
}
base := "https://127.0.0.1" // I want to dial this IP
hostHeader := "nonexistent-host.invalid" // placeholder domain, not expected to resolve
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
req.Header.SetMethod("GET")
req.SetRequestURI(base + "/test")
req.SetHost(hostHeader) // or req.Header.SetHost(hostHeader)
err := client.DoTimeout(req, resp, 2*time.Second)
fmt.Println("err:", err)
fmt.Printf("dial addrs: %#v\n", dialAddrs)
}Typical output (simplified):
err: stop
dial addrs: []string{"nonexistent-host.invalid:443"}
So even though the URL contained 127.0.0.1, the dial target becomes nonexistent-host.invalid:443, which shows that the Host value is used for dial target selection.
Expected / desired behavior
For this use–case I would like:
SetRequestURI("https://127.0.0.1/test")to determine the dial target (127.0.0.1:443)SetHost("nonexistent-host.invalid")(or similar) to only change the HTTPHostheader- The dial target should remain
127.0.0.1:443and not use the Host value for DNS/dial
This matches how net/http behaves:
req, _ := http.NewRequest("GET", "https://127.0.0.1/test", nil)
req.Host = "nonexistent-host.invalid" // only overrides header
client.Do(req) // still dials 127.0.0.1:443Why existing options don’t fully solve it
- I could create a separate
HostClientper host, but in my tool there can be many different Host values (user–controlled dictionaries), so this is not practical. - Custom
Dialcan hardcode an IP, but then I lose the per–request flexibility of a singleClientthat respects the URI’s host:port. - Issues req.Header.SetHost() doesn't work within req.SetRequestURI in fasthttp.Client #904 and Setting host header not working with HTTPS #841 discuss
HostClient, SNI, and related behavior, but I haven’t found a clean way to say: “trust the URI for dial, and treat Host only as a header override”.
Feature request / API idea
Would it be possible to add an API or option that:
- Lets us set the Host header without changing the dial target, and
- Keeps current behavior as default for backward compatibility.
For example (just ideas, not a strict proposal):
- A method like
req.Header.SetHostOnly(host string)that only affects the header. - Or a flag on
Request/Client, e.g.DisableHostHeaderDialorUseURIHostForDial.
I’m happy to adjust my code to any recommended pattern, but right now I don’t see a way to get the net/http–style behavior with a single fasthttp.Client and per–request Host overrides.
Is there an existing pattern I missed, or would you consider adding such an API?
Thanks!