How to Terminate openssl s_client After HTTP GET Request Completion


2 views

When using openssl s_client for HTTP requests, many developers encounter the frustrating behavior where the command doesn't terminate after receiving the response. The root cause lies in SSL's connection persistence combined with the -quiet flag's implicit -ign_eof setting.

Here are three reliable approaches to force termination while still capturing the response:

Method 1: Timeout with head

echo -e "GET /test HTTP/1.1\\r\\nHost:$(hostname)\\r\\nConnection: close\\r\\n\\r\\n" | \
openssl s_client -quiet -connect server-url:443 | \
(head -n1 > /dev/null && cat) | \
grep -o -P '(?<=Result>).*(?=)'

Method 2: HTTP/1.0 with Connection: close

echo -e "GET /test HTTP/1.0\\r\\nHost:$(hostname)\\r\\nConnection: close\\r\\n\\r\\n" | \
openssl s_client -quiet -connect server-url:443 | \
grep -o -P '(?<=Result>).*(?=)'

Method 3: Content-Length Parsing

For more complex responses where you need to read exactly the right amount of data:

response=$(echo -e "GET /test HTTP/1.1\\r\\nHost:$(hostname)\\r\\n\\r\\n" | \
openssl s_client -quiet -connect server-url:443)

content_length=$(echo "$response" | grep -i 'content-length:' | awk '{print $2}' | tr -d '\r')
if [ -n "$content_length" ]; then
    echo "$response" | head -c $((${#response} - $(echo "$response" | wc -c) + $content_length + 1))
else
    echo "$response"
fi | grep -o -P '(?<=Result>).*(?=)'

When implementing this in production scripts:

  • Always add timeout protection
  • Validate SSL certificates unless in controlled environments
  • Consider using curl instead if available (though openssl is valuable in minimal environments)

If you're still seeing hanging connections:

# Add full HTTP headers for debugging
echo -e "GET /test HTTP/1.1\\r\\nHost:$(hostname)\\r\\nUser-Agent: openssl-test\\r\\nAccept: */*\\r\\nConnection: close\\r\\n\\r\\n" | \
openssl s_client -debug -connect server-url:443

Remember that some servers may ignore HTTP/1.1's Connection: close header, making HTTP/1.0 the more reliable choice for immediate termination.


When using OpenSSL's s_client for HTTP requests, many developers encounter the frustrating behavior where the connection remains open indefinitely after receiving the response. This occurs because:

  • -quiet mode implicitly sets -ign_eof
  • HTTP/1.1 connections are persistent by default
  • The server waits for additional requests

Here are three working approaches to force connection closure:

1. Using Connection: close header

printf "GET /test HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" | \
openssl s_client -quiet -connect server-url:443 2>/dev/null

2. Forcing HTTP/1.0 protocol

printf "GET /test HTTP/1.0\r\nHost: example.com\r\n\r\n" | \
openssl s_client -quiet -connect server-url:443 2>/dev/null

3. Timeout-based solution

echo -e "GET /test HTTP/1.1\r\nHost: example.com\r\n\r\n" | \
timeout 5 openssl s_client -quiet -connect server-url:443

For parsing XML responses like <Result>success</Result>:

response=$(printf "GET /test HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n" | \
openssl s_client -quiet -connect server-url:443 2>/dev/null)

# Extract content between Result tags
result=$(echo "$response" | grep -oP '(?<=<Result>).*(?=</Result>)')
echo "Server response: $result"

Here's a complete bash function for reliable HTTPS requests:

https_get() {
    local host="$1"
    local path="$2"
    local port="${3:-443}"
    
    exec 3<> /dev/tcp/"$host"/"$port"
    printf "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n" "$path" "$host" >&3
    cat <&3
    exec 3>&-
}

# Usage:
response=$(https_get "example.com" "/api/test")