Skip to content

Eine HTTP-GET-Anfrage mit Timeout senden snippet

Eine Anfrage ohne Timeout kann ewig hängen — die meisten Clients stehen standardmäßig auf Minuten, manche auf Unendlich.

Eine Anfrage ohne Timeout kann ewig hängen — die meisten Clients stehen standardmäßig auf Minuten, manche auf Unendlich. Jede Sprache verkabelt die Deadline anders: ein Signal am fetch (JavaScript), ein Context an der Anfrage (Go), eine Duration am Client-Builder (Rust), ein Token an jedem await (C#), zwei getrennte Dials in Ruby und Java. C, C++ und Zig fehlen: keine davon liefert in der Standardbibliothek einen HTTP-Client mit brauchbarem Timeout-Regler.

Runnable recipe · 11 languages
HTTP & APIshttpgetfetchtimeoutnetworking

Every language

11 languages, copy-ready. One at a time with syntax highlighting, or all inline.

JSJavaScript
// AbortSignal.timeout covers the whole exchange — headers AND body read.
const res = await fetch('https://api.example.com/status', {
  signal: AbortSignal.timeout(5000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
console.log(await res.text());

fetch does NOT reject on 4xx/5xx — check res.ok yourself. Node 17.3+ and all modern browsers have AbortSignal.timeout.

TSTypeScript
interface Status {
  ok: boolean;
  uptime: number;
}

const res = await fetch('https://api.example.com/status', {
  signal: AbortSignal.timeout(5000),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const status = (await res.json()) as Status; // cast is trust — validate untrusted APIs
console.log(status.uptime);

The as-cast silences the type checker, it does not check anything at runtime. Parse untrusted payloads with a schema library (Zod, Valibot).

GoGo
package main

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"time"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet,
		"https://api.example.com/status", nil)
	if err != nil {
		panic(err)
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err) // wraps context.DeadlineExceeded when the deadline fires
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}

The context deadline covers dial, TLS, headers AND the body read. http.Client{Timeout: 5 * time.Second} is the coarser whole-client alternative.

RsRust
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    let client = reqwest::Client::builder()
        .timeout(Duration::from_secs(5))
        .build()?;

    let body = client
        .get("https://api.example.com/status")
        .send()
        .await?
        .error_for_status()? // 4xx/5xx become Err, not a silent response
        .text()
        .await?;

    println!("{body}");
    Ok(())
}

reqwest is the de-facto Rust HTTP client (hyper underneath). Client-level timeout applies per request made through that client — reuse one client, not one per call.

PHPPHP
$ctx = stream_context_create(['http' => ['timeout' => 5]]);
$body = file_get_contents('https://api.example.com/status', false, $ctx);

if ($body === false) {
    throw new RuntimeException('request failed or timed out');
}
echo $body;

The stream wrapper is stdlib but ignores the HTTP status by default — add 'ignore_errors' => true to read error bodies. cURL or Guzzle is the production answer.

PyPython
import httpx

response = httpx.get('https://api.example.com/status', timeout=5.0)
response.raise_for_status()
print(response.text)

timeout is seconds as a float — 5.0, not 5 (requests has the same kwarg). It raises httpx.TimeoutException, no extra wiring needed.

C#C#
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

using var client = new HttpClient();
using var response = await client.GetAsync(
    "https://api.example.com/status",
    HttpCompletionOption.ResponseHeadersRead,
    cts.Token);

response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync(cts.Token);
Console.WriteLine(body);

The token must reach EVERY await — GetAsync and ReadAsStringAsync both take it. ResponseHeadersRead scopes the deadline to the headers, not the full download.

JvJava
import java.net.URI;
import java.net.http.*;
import java.time.Duration;

HttpClient client = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(5))
        .build();

HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/status"))
        .timeout(Duration.ofSeconds(5)) // covers send() to response headers
        .GET()
        .build();

HttpResponse<String> response = client.send(request,
        HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
    throw new IllegalStateException("HTTP " + response.statusCode());
}
System.out.println(response.body());

Two dials: connectTimeout on the client, timeout on the request. java.net.http is JDK 11+ — older HttpURLConnection has no request timeout at all.

SwSwift
import Foundation

var request = URLRequest(url: URL(string: "https://api.example.com/status")!)
request.timeoutInterval = 5 // seconds, whole request

let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse,
      (200..<300).contains(http.statusCode) else {
    throw URLError(.badServerResponse)
}
print(String(decoding: data, as: UTF8.self))

URLRequest.timeoutInterval wins over the session's timeoutIntervalForRequest for that request. data(for:) is the async/await API (Swift 5.5+).

KtKotlin
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.Duration

val client = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(5))
    .build()

val request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/status"))
    .timeout(Duration.ofSeconds(5))
    .GET()
    .build()

val response = client.send(request, HttpResponse.BodyHandlers.ofString())
check(response.statusCode() < 400) { "HTTP ${response.statusCode()}" }
println(response.body())

Same java.net.http as Java. Ktor's HttpClient is the multiplatform alternative — its HttpTimeout plugin sets connect/request/socket deadlines in one config.

RbRuby
require 'net/http'
require 'uri'

uri = URI('https://api.example.com/status')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.open_timeout = 5 # seconds to open the connection
http.read_timeout = 5 # seconds per socket read AFTER it opens

response = http.get(uri.request_uri)
puts response.body if response.is_a?(Net::HTTPSuccess)

Net::HTTP has no single whole-request dial: open_timeout and read_timeout are separate, and read_timeout is per-read, not total.