Implementing a Custom DNS Resolver for Go HTTP Client Testing

This article demonstrates how to extend Go's HTTP client with a custom DNS resolver using net.Dialer, configure load‑balancing, and test the implementation with sample code and a simple mock service, providing a practical guide for backend developers.

FunTester
FunTester
FunTester
Implementing a Custom DNS Resolver for Go HTTP Client Testing

After completing Java custom DNS resolver articles, I explored the same functionality for Go's HTTP testing, ultimately implementing a custom DNS resolver using the standard http library (with fasthttp to be covered later).

Setting net.Dialer

First, the net.Dialer (the dialing component for establishing HTTP connections) is configured similarly to Java's HttpClient connection manager.

func clients() http.Client {
    dialer := &net.Dialer{Timeout: 1 * time.Second}
    return http.Client{
        Timeout: 5 * time.Second,
        Transport: &http.Transport{
            MaxIdleConnsPerHost:   200,
            MaxConnsPerHost:       10000,
            IdleConnTimeout:       90 * time.Second,
            TLSHandshakeTimeout:  10 * time.Second,
            ExpectContinueTimeout: 1 * time.Second,
            DialContext:           dialer.DialContext,
        },
    }
}

Strange Knowledge Point

Go's net/http library also supports binding a DNS service IP, which can direct requests for a fixed domain to a specific machine.

dialer := &net.Dialer{Timeout: 1 * time.Second}
 dialer.Resolver = &net.Resolver{Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
    return dialer.DialContext(ctx, "tcp", "114.114.114.114:53") // use custom nameserver
 },}

Custom net.Dialer

The http.Transport struct includes a DialContext field, a function used to create raw TCP connections. By providing a custom closure, we can intercept connections and perform DNS‑based load balancing.

DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
    host, port, err := net.SplitHostPort(address)
    if err != nil { return nil, err }
    // Example: redirect "fun.tester" to 127.0.0.1
    if host == "fun.tester" {
        ip := "127.0.0.1"
        log.Println(ip)
        conn, err := dialer.DialContext(ctx, network, ip+":"+port)
        if err == nil { return conn, nil }
    }
    return dialer.DialContext(ctx, network, address)
},

Test

A simple test case creates a request to the custom host and prints the response.

// TestFaast
func TestFaast(t *testing.T) {
    url := "http://fun.tester:12345/test"
    get := fhttp.Get(url, nil)
    response := fhttp.Response(get)
    log.Println(string(response))
}

Console output shows the request being routed to 127.0.0.1 and the expected response.

=== RUN   TestFaast
2022/02/07 15:34:47 127.0.0.1
2022/02/07 15:34:48 Have Fun ~ Tester !
--- PASS: TestFaast (0.31s)
PASS

Test Service

The test service is a simple mock built with the moco_FunTester framework.

static void main(String[] args) {
    def util = new ArgsUtil(args)
    def server = getServerNoLog(util.getIntOrdefault(0, 12345))
    server.response(delay(textRes("Have Fun ~ Tester !"), 100))
    def run = run(server)
    waitForKey("fan")
    run.stop()
}

FunTester 2021 Summary

FunTester Original Works

Groovy Language Notes

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

testingload balancingGoDNSnet.Dialerhttp-client
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.