When working with HTTP requests in Go, it's important to set a timeout to prevent your application from waiting indefinitely for a response. A timeout ensures that your application can recover or handle the situation gracefully in case of network issues or unresponsive servers.
Using net/http Client Timeout
In Go, the http.Client has a Timeout field that can be used to set a deadline for the entire HTTP request.
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
client := &http.Client{
Timeout: 10 * time.Second, // Set request timeout to 10 seconds
}
resp, err := client.Get("https://www.example.com")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response received with status code:", resp.StatusCode)
}
In this example, we create an http.Client and specify a 10-second timeout. If the request takes longer, it will return an error.
Dial and TLS Handshake Timeouts
To set more specific timeouts, you can customize the http.Transport. This is useful when you want to have separate timeouts for establishing a connection, writing the request, and getting the response headers.
package main
import (
"fmt"
"net/http"
"net"
"time"
)
func main() {
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // Maximum time allowed to establish a connection
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second, // Max time for TLS handshake
}
client := &http.Client{
Transport: transport,
Timeout: 10 * time.Second, // Overall timeout for request
}
resp, err := client.Get("https://www.example.com")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response received with status code:", resp.StatusCode)
}
Here, the DialContext and TLSHandshakeTimeout ensure that connecting to the server and completing the TLS handshake each has a maximum duration of 5 seconds.
Custom Timeout Using Context Package
For even more control, especially when dealing with complex requests involving multiple parts or different timeouts for different parts, you can use the context package to set a deadline or timeout for requests.
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.example.com", nil)
if err != nil {
fmt.Println("Error creating request:", err)
return
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error executing request:", err)
return
}
defer resp.Body.Close()
fmt.Println("Response received with status code:", resp.StatusCode)
}
Using the context package, you can provide a 5-second timeout for this HTTP request. The NewRequestWithContext function is used to attach the context to the request. This method is versatile and can be adjusted for multiple operations or even completely different timeout policies for each request.
Conclusion
Setting appropriate timeouts for HTTP requests is essential for robust and responsive applications. Using http.Client with a Timeout field, customizing transport settings, or using the context package provides flexibility for different scenarios. Always consider the nature of your HTTP interactions to set optimal timeout values that suit your application's needs.