使用golang终止IP层的http请求

I am making an http post request to a server using golang. Suppose the server is currently turned off (Means the machine on which the server runs is turned off) then the request is stuck at the IP layer. So my program execution is unable to proceed further. It is unable to proceed to the Application layer. So is there any way in golang to stop this. I am using the following code.

req, err := http.NewRequest("POST", url, bytes.NewReader(b))
       if err != nil {
               return errors.Wrap(err, "new request error")
       }
resp, err := http.DefaultClient.Do(req)
       if err != nil {
               return errors.Wrap(err, "http request error")
       }
       defer resp.Body.Close()

Is there anything that can be added to this, to terminate the request if it doesn't find anything from the IP layer.

The default http Client has no timeout. You can create an explicit http.Client yourself and set the timeout:

var cl = &http.Client{
    Timeout: time.Second * 10,
}

resp, err := cl.Do(req)

if err != nil {
    // err will be set on timeout
    return errors.Wrap(err, "http request error")
}
defer resp.Body.Close()

If the server does not answer any more in the middle of a request, you can handle the timeout.

Use a non-default http.Transport with its DialContext field set to a function which uses a custom context with the properly configured timeout/deadline. Another option is to use a custom net.Dialer.

Something like this:

cli := http.Client{
    Transport: &http.Transport{
        DialContext: func (ctx context.Context, network, address string) (net.Conn, error) {
            dialer := net.Dialer{
                Timeout: 3 * time.Second,
            }
            return dialer.DialContext(ctx, network, address)
        },
    },
}

req, err := http.NewRequest(...)
resp, err := cli.Do(req)

Note that as per the net.Dialer's docs the context passed to its DialContext might trump the timeout set on the dialer itself—this is exactly what we need: the dialer's Timeout field controls exactly the "dialing" (TCP connection establishment) while you might also arm your HTTP request with a context (using http.Request.WithContext) controlling the timeout of the whole request, and also be able to cancel it at any time (including the dialing step).

Playground example.

The Transport @kostix refers to is definitely what you're looking for in this case. Transports as well as Clients are safe for concurrent use as well. But please read about the Transport (and I also advise reading about the Client as well) as there are a number of different ways to affect how you handle idle connections, not just the pre-mentioned DialContext.

As you may want to set your ResponseHeaderTimeout:

ResponseHeaderTimeout, if non-zero, specifies the amount of time to wait for a server's response headers after fully writing the request (including its body, if any). This time does not include the time to read the response body.

Or, if you are using a secure connection, you may want to set your TLSHandshakeTimeout:

TLSHandshakeTimeout specifies the maximum amount of time waiting to wait for a TLS handshake. Zero means no timeout.

For readability and maintainability, I suggest also maybe creating a function to build your Client, something along the lines of:

func buildClient(timeout time.Duration) *http.Client {
    tr := &http.Transport{
        IdleConnTimeout:       timeout,
        ResponseHeaderTimeout: timeout,
        TLSHandshakeTimeout:   timeout,
    }
    client := &http.Client{
        Transport: tr,
        Timeout:   timeout,
    }
    return client
}