빌드노트
curl에 connect-timeout을 줬는데 응답을 계속 기다리는 이유
curl의 connect-timeout은 연결 이후의 응답 대기를 제한하지 않는다. 헤더·본문을 각각 2초 늦춘 로컬 서버에서 max-time 유무의 종료 코드와 시간을 비교하고, 재시도마다 다시 시작하는 제한과 retry-max-time을 넘길 수 있는 진행 중 전송을 구별한다.
curl --connect-timeout 0.5를 줬는데 응답을 2초 넘게 기다린다면, 연결 뒤의 대기 시간을 봐야 한다. 이 옵션은 연결 단계만 제한한다. 응답 본문을 다 받을 때까지 한 번의 전송을 제한하려면 --max-time을 함께 준다.
Windows 11과 curl 8.21.0에서 연결을 바로 받는 로컬 HTTP 서버로 비교했다. 헤더를 보내기 전에 2초 기다리는 경우와, 200 헤더를 먼저 보내고 3바이트 본문을 보내기 전에 2초 기다리는 경우를 만들었다. 모든 요청에 --connect-timeout 0.5를 주고 --max-time 1의 유무만 바꾼 실제 결과다. 시간 단위는 초다.
exit HTTP time_connect time_total
헤더 지연, 연결 제한만 0 200 0.000583 2.001422
헤더 지연, 전체 제한 추가 28 000 0.000605 1.015473
본문 지연, 연결 제한만 0 200 0.000619 2.001435
본문 지연, 전체 제한 추가 28 200 0.000649 1.010213
연결은 네 경우 모두 0.5초 안에 끝났다. 연결 제한만 두면 응답을 2초 기다렸고, 전체 제한을 더하면 약 1초에 중단됐다. --connect-timeout 0.5 --max-time 1의 허용 시간이 1.5초가 되는 것은 아니다. 연결에 쓴 시간도 전체 전송의 1초 안에 들어간다. 연결 제한 (새 탭에서 열림), 전송 제한 (새 탭에서 열림)
마지막 줄의 HTTP 200과 종료 코드 28은 함께 나올 수 있다. 헤더는 받았지만 본문은 한 바이트도 받지 못한 채 시간 초과로 끝났다. HTTP 상태만 보고 성공으로 처리하면 이 실패를 놓친다. 28은 curl의 시간 초과 종료 코드이고, 위의 000은 응답 코드를 받지 못했을 때의 출력값이지 실제 HTTP 상태가 아니다. curl 종료 코드 (새 탭에서 열림)
내 URL에서는 시간과 종료 코드를 함께 본다
다음은 Windows의 PowerShell 7과 curl.exe를 쓰는 진단 명령이다. $url을 자신이 확인할 읽기 전용 GET 주소로 바꾼다. 본문은 NUL로 버리고 상태와 시간만 출력한다. 첫 옵션인 -q는 기본 curl 설정 파일을 읽지 않게 한다. 0.5초와 1초는 비교용 값이므로 실제 요청의 정상 응답 시간에 맞춰 정한다.
$url = 'https://your-test.example/health'
curl.exe -q -sS -o NUL --connect-timeout 0.5 --max-time 1 -w 'http=%{http_code} connect=%{time_connect} app=%{time_appconnect} start=%{time_starttransfer} total=%{time_total}\n' $url
"exit=$LASTEXITCODE"
앞의 HTTP 비교에서는 connect가 연결 완료까지, start가 첫 응답 바이트까지, total이 전송 완료 또는 중단까지 걸린 시간이다. 헤더 지연은 start가 약 2초였고, 본문 지연은 start가 약 0.001초인데 total이 약 2초였다. 헤더를 받기 전에 중단된 요청의 start=0은 즉시 응답했다는 뜻이 아니다.
HTTPS에서는 connect가 짧다는 사실만으로 연결 단계가 끝났다고 판단하지 않는다. --connect-timeout에는 DNS 조회와 필요한 TCP·TLS·QUIC 핸드셰이크가 포함된다. TCP 연결 완료인 connect 외에 TLS 핸드셰이크 완료 시점인 app도 본다. 일반 HTTP에서 app=0은 TLS를 사용하지 않은 결과다. 프록시를 거치면 connect는 프록시까지의 TCP 연결 시간일 수도 있다. 시간 지표 정의 (새 탭에서 열림)
응답이 1초 안에 끝나면 이 명령도 정상 종료한다. 종료 코드가 28이면 시간 지표와 오류 메시지로 어디까지 진행했는지 본다. DNS 조회 실패나 연결 실패라면 이 글의 응답 지연과 다른 문제이므로 주소·접속 경로부터 확인한다. 이 비교를 다시 하려고 연결 제한만 남기면 응답 대기에는 상한이 없어지므로, 호출하는 도구에도 종료 제한을 둔다.
재시도를 켜면 명령 전체는 1초를 넘을 수 있다
--retry를 켜면 --max-time의 시계는 재시도마다 다시 시작한다. 시간 초과도 재시도 대상이며, --retry 2는 첫 시도에 더해 최대 두 번을 다시 시도한다는 뜻이다. 재시도 횟수와 조건 (새 탭에서 열림)
같은 헤더 지연에 --max-time 1 --retry 2 --retry-delay 1을 적용하자 요청이 3번 도착했고 프로세스 전체는 약 5.06초 걸렸다. 마지막 time_total 출력은 약 1.01초였다. 이 버전에서 그 값은 재시도 대기까지 포함한 프로세스 전체 시간과 달랐다.
여기에 --retry-max-time 2.5를 더하면 요청은 2번, 전체 시간은 약 3.05초였다. 재시도 기간 2.5초는 실행 중인 전송을 그 시각에 끊는 마감이 아니다. 첫 시도 약 1초와 대기 1초 뒤 두 번째가 시작됐고, 그 전송은 자신의 1초 제한까지 진행했다. 재시도 시계는 첫 시도 직전에 시작하며 대기 시간도 포함하지만, 이미 시작한 전송은 기간을 넘길 수 있다. 소수 초 지정은 curl 8.16.0부터 지원한다. 재시도 기간의 범위 (새 탭에서 열림)
연결 단계에는 --connect-timeout, 한 번의 전송에는 --max-time, 재시도를 시작할 기간에는 --retry-max-time을 정한다. 재시도 대기까지 포함해 호출 작업 전체를 정해진 시간 안에 끝내야 한다면 실행 도구나 호출 코드에도 별도의 상위 제한이 필요하다. 값을 바꾸는 요청에 재시도를 추가할지는 중복 실행 가능성을 따로 판단한다.
직접 실행한 범위는 IPv4 loopback HTTP의 짧은 고정 지연이며, 지연 없는 대조 요청은 두 설정 모두 정상 종료했다. 위 시간은 각 조건 한 번의 관찰로, 일정한 타이머 오차를 보장하지 않는다. DNS 지연, TLS·QUIC 지연, 프록시와 외부 네트워크는 실험하지 않았다. 연결 단계와 HTTPS 지표 설명은 설치 버전의 공식 문서를 확인한 내용이다.