Overview
The HTTP request/response cycle describes the complete process of communication between a client and server using the Hypertext Transfer Protocol. When a client needs data from a server, it initiates this cycle by sending an HTTP request message. The server processes the request and returns an HTTP response message containing the requested resource or an error status. This cycle forms the foundation of web communication and API interactions.
The cycle operates at the application layer of the TCP/IP model. Each cycle is stateless, meaning the server retains no memory of previous requests from the same client. This stateless design enables scalability but requires additional mechanisms like cookies or tokens to maintain user sessions across multiple request/response cycles.
A typical cycle follows this sequence: the client establishes a TCP connection to the server, sends an HTTP request, waits for the server to process the request, receives the HTTP response, and closes or reuses the connection. Modern HTTP implementations support persistent connections that allow multiple request/response cycles over a single TCP connection, reducing overhead.
require 'net/http'
require 'uri'
# Basic HTTP GET request demonstrating the cycle
uri = URI('https://api.example.com/users/1')
response = Net::HTTP.get_response(uri)
puts "Status: #{response.code}"
puts "Body: #{response.body}"
# Status: 200
# Body: {"id":1,"name":"John Doe"}
Understanding this cycle helps developers debug network issues, optimize application performance, implement proper error handling, and design secure APIs. The cycle's mechanics directly impact latency, throughput, security, and reliability of distributed systems.
Key Principles
Request Structure: An HTTP request consists of three main components: the request line, headers, and an optional body. The request line specifies the HTTP method (GET, POST, PUT, DELETE, etc.), the target URI path, and the protocol version. Headers provide metadata about the request such as content type, authentication credentials, and caching directives. The body contains data sent to the server, primarily used with POST, PUT, and PATCH methods.
Response Structure: HTTP responses mirror the request structure with a status line, headers, and optional body. The status line includes the protocol version, a three-digit status code, and a reason phrase. Status codes range from 100-599, categorized into informational (1xx), successful (2xx), redirection (3xx), client errors (4xx), and server errors (5xx). Response headers convey information about the server, caching policies, and content characteristics.
Connection Management: The request/response cycle depends on an underlying TCP connection. HTTP/1.0 used a new connection for each request/response cycle, creating significant overhead. HTTP/1.1 introduced persistent connections through the Connection: keep-alive header, allowing connection reuse. HTTP/2 multiplexes multiple request/response pairs over a single TCP connection simultaneously, eliminating head-of-line blocking at the HTTP layer.
Statelessness: Each request contains all information needed for the server to process it. The server does not maintain client state between requests. Session management requires explicit mechanisms like cookies, URL parameters, or authorization tokens. This design simplifies server architecture and enables horizontal scaling but shifts state management responsibility to the client or external storage systems.
Content Negotiation: Clients and servers negotiate content representation through headers. The Accept header indicates preferred response formats (application/json, text/html). The Accept-Language header specifies language preferences. The Accept-Encoding header requests compression formats. Servers select appropriate representations based on these preferences and their capabilities, returning 406 Not Acceptable if no suitable representation exists.
Idempotency: HTTP methods have defined idempotency guarantees. GET, PUT, DELETE, HEAD, OPTIONS, and TRACE are idempotent—multiple identical requests produce the same server state as a single request. POST and PATCH are not idempotent. Understanding idempotency is critical for retry logic and error recovery.
require 'net/http'
# Demonstrating request components
uri = URI('https://api.example.com/posts')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer token123'
request.body = { title: 'New Post', content: 'Content here' }.to_json
response = http.request(request)
# Request line: POST /posts HTTP/1.1
# Headers: Content-Type, Authorization
# Body: JSON payload
Redirects: Servers use 3xx status codes to redirect clients to different URIs. 301 indicates permanent relocation, 302 indicates temporary redirection. Clients automatically follow redirects, but the number of redirects should be limited to prevent infinite loops. The Location header specifies the redirect target.
Conditional Requests: Clients can make requests conditional on resource state using headers like If-Modified-Since, If-None-Match, and If-Match. These enable caching and prevent race conditions. Servers respond with 304 Not Modified if the resource hasn't changed, saving bandwidth.
Ruby Implementation
Ruby provides multiple approaches for implementing HTTP clients, from low-level standard library classes to high-level abstraction gems. The Net::HTTP class in the standard library handles basic HTTP operations without external dependencies. It manages TCP connections, constructs request messages, and parses response messages.
Net::HTTP Standard Library: This class provides direct control over HTTP request construction and response handling. Developers create URI objects, initialize HTTP instances, configure SSL settings, build request objects, and execute the request/response cycle.
require 'net/http'
require 'json'
# Detailed Net::HTTP usage
uri = URI('https://api.github.com/users/octocat')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.open_timeout = 5 # Connection timeout
http.read_timeout = 10 # Read timeout
request = Net::HTTP::Get.new(uri.request_uri)
request['Accept'] = 'application/vnd.github.v3+json'
request['User-Agent'] = 'Ruby HTTP Client'
response = http.request(request)
case response
when Net::HTTPSuccess
data = JSON.parse(response.body)
puts "User: #{data['login']}"
when Net::HTTPRedirection
puts "Redirected to: #{response['location']}"
when Net::HTTPClientError
puts "Client error: #{response.code}"
when Net::HTTPServerError
puts "Server error: #{response.code}"
end
HTTParty Gem: HTTParty simplifies HTTP interactions with a declarative API. It handles JSON parsing, query parameter encoding, and response object wrapping automatically. The gem reduces boilerplate for common HTTP operations.
require 'httparty'
class GitHubClient
include HTTParty
base_uri 'https://api.github.com'
def initialize(token)
@options = {
headers: {
'Authorization' => "token #{token}",
'User-Agent' => 'Ruby HTTParty Client'
}
}
end
def user(username)
self.class.get("/users/#{username}", @options)
end
def create_issue(repo, title, body)
self.class.post(
"/repos/#{repo}/issues",
@options.merge(body: { title: title, body: body }.to_json)
)
end
end
client = GitHubClient.new('token123')
response = client.user('octocat')
puts response.parsed_response['name'] if response.success?
Faraday Gem: Faraday provides middleware-based HTTP client architecture. Middleware components handle concerns like authentication, logging, retry logic, and response parsing. This design enables composition and reuse of HTTP client behaviors.
require 'faraday'
require 'faraday/retry'
# Faraday with middleware stack
conn = Faraday.new(url: 'https://api.example.com') do |f|
f.request :json # Encode request body as JSON
f.request :retry, max: 3, interval: 0.5
f.response :json # Parse response body as JSON
f.response :logger # Log requests and responses
f.adapter Faraday.default_adapter
end
response = conn.get('/users/1') do |req|
req.headers['Authorization'] = 'Bearer token123'
req.params['include'] = 'posts,comments'
end
puts response.body['name'] if response.success?
Rack Interface: Rack defines a minimal interface between web servers and Ruby applications. While primarily used for server-side request handling, understanding Rack helps grasp how HTTP requests map to Ruby data structures. Rack represents requests as environment hashes and responses as arrays.
# Rack application structure
class SimpleRackApp
def call(env)
request = Rack::Request.new(env)
# Access request components
method = request.request_method # GET, POST, etc.
path = request.path_info
params = request.params
headers = request.env.select { |k,v| k.start_with?('HTTP_') }
# Build response
status = 200
headers = { 'Content-Type' => 'application/json' }
body = [{ message: 'Hello', path: path }.to_json]
[status, headers, body]
end
end
# Rack environment hash example
env = {
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/users/1',
'QUERY_STRING' => 'format=json',
'HTTP_ACCEPT' => 'application/json',
'rack.input' => StringIO.new
}
Async HTTP with async-http Gem: For high-concurrency scenarios, the async-http gem leverages Ruby's Fiber-based concurrency to handle multiple HTTP requests efficiently without blocking threads.
require 'async'
require 'async/http/internet'
Async do
internet = Async::HTTP::Internet.new
# Concurrent requests
responses = [
'https://api.example.com/users/1',
'https://api.example.com/users/2',
'https://api.example.com/users/3'
].map do |url|
Async do
internet.get(url)
end
end.map(&:wait)
responses.each do |response|
puts "Status: #{response.status}"
puts "Body: #{response.read}"
end
ensure
internet&.close
end
Practical Examples
API Client with Error Handling: Real-world HTTP clients require comprehensive error handling for network failures, timeouts, invalid responses, and rate limiting. This example demonstrates a production-ready approach.
require 'net/http'
require 'json'
require 'uri'
class ResilientAPIClient
MAX_RETRIES = 3
RETRY_DELAY = 1
def initialize(base_url, api_key)
@base_url = base_url
@api_key = api_key
end
def get(path, params = {})
uri = build_uri(path, params)
request = build_request(Net::HTTP::Get, uri)
execute_with_retry(uri, request)
end
def post(path, body)
uri = build_uri(path)
request = build_request(Net::HTTP::Post, uri)
request['Content-Type'] = 'application/json'
request.body = body.to_json
execute_with_retry(uri, request)
end
private
def build_uri(path, params = {})
uri = URI.join(@base_url, path)
uri.query = URI.encode_www_form(params) unless params.empty?
uri
end
def build_request(request_class, uri)
request = request_class.new(uri.request_uri)
request['Authorization'] = "Bearer #{@api_key}"
request['Accept'] = 'application/json'
request['User-Agent'] = 'ResilientClient/1.0'
request
end
def execute_with_retry(uri, request, attempt = 1)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.open_timeout = 5
http.read_timeout = 10
response = http.request(request)
case response
when Net::HTTPSuccess
JSON.parse(response.body)
when Net::HTTPTooManyRequests
raise "Rate limited" if attempt >= MAX_RETRIES
sleep(response['Retry-After']&.to_i || RETRY_DELAY * attempt)
execute_with_retry(uri, request, attempt + 1)
when Net::HTTPServerError
raise "Server error" if attempt >= MAX_RETRIES
sleep(RETRY_DELAY * attempt)
execute_with_retry(uri, request, attempt + 1)
when Net::HTTPClientError
raise "Client error: #{response.code} - #{response.body}"
else
raise "Unexpected response: #{response.code}"
end
rescue Net::OpenTimeout, Net::ReadTimeout => e
raise "Timeout" if attempt >= MAX_RETRIES
sleep(RETRY_DELAY * attempt)
execute_with_retry(uri, request, attempt + 1)
rescue SocketError, Errno::ECONNREFUSED => e
raise "Connection failed: #{e.message}"
end
end
# Usage
client = ResilientAPIClient.new('https://api.example.com', 'key123')
begin
user_data = client.get('/users/1', { include: 'posts' })
puts "User: #{user_data['name']}"
rescue => e
puts "Request failed: #{e.message}"
end
Streaming Large Response Bodies: When downloading large files or processing streaming APIs, loading the entire response body into memory causes issues. Ruby supports streaming responses in chunks.
require 'net/http'
def download_file(url, destination)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri.request_uri)
http.request(request) do |response|
unless response.is_a?(Net::HTTPSuccess)
raise "Download failed: #{response.code}"
end
total_size = response['Content-Length']&.to_i || 0
downloaded = 0
File.open(destination, 'wb') do |file|
response.read_body do |chunk|
file.write(chunk)
downloaded += chunk.bytesize
print "\rDownloaded: #{downloaded} / #{total_size} bytes"
end
end
puts "\nDownload complete"
end
end
end
download_file('https://example.com/large-file.zip', 'output.zip')
Connection Pool Management: Applications making frequent HTTP requests benefit from connection pooling. Reusing TCP connections eliminates handshake overhead. This example shows persistent connection management.
require 'net/http'
class ConnectionPool
def initialize(host, port, pool_size: 5)
@host = host
@port = port
@pool_size = pool_size
@pool = []
@mutex = Mutex.new
end
def with_connection
connection = acquire_connection
yield connection
ensure
release_connection(connection)
end
private
def acquire_connection
@mutex.synchronize do
if @pool.empty?
create_connection
else
@pool.pop
end
end
end
def release_connection(connection)
@mutex.synchronize do
if @pool.size < @pool_size && connection.started?
@pool.push(connection)
else
connection.finish if connection.started?
end
end
end
def create_connection
http = Net::HTTP.new(@host, @port)
http.use_ssl = true
http.start
http
end
end
# Usage
pool = ConnectionPool.new('api.example.com', 443, pool_size: 5)
10.times do |i|
pool.with_connection do |http|
request = Net::HTTP::Get.new("/users/#{i}")
response = http.request(request)
puts "Request #{i}: #{response.code}"
end
end
WebSocket Upgrade from HTTP: WebSockets begin with an HTTP request that upgrades the connection. This example demonstrates the upgrade handshake.
require 'socket'
require 'digest/sha1'
require 'base64'
# Simplified WebSocket client handshake
def websocket_handshake(host, port, path)
socket = TCPSocket.new(host, port)
# Generate WebSocket key
key = Base64.strict_encode64(Random.bytes(16))
# Send HTTP upgrade request
request = [
"GET #{path} HTTP/1.1",
"Host: #{host}:#{port}",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Key: #{key}",
"Sec-WebSocket-Version: 13",
"\r\n"
].join("\r\n")
socket.write(request)
# Read response
response = ""
while line = socket.gets
response += line
break if line == "\r\n"
end
# Verify upgrade response
if response.include?('101 Switching Protocols')
puts "WebSocket connection established"
socket
else
socket.close
raise "WebSocket upgrade failed"
end
end
socket = websocket_handshake('echo.websocket.org', 80, '/')
# WebSocket connection established
socket.close
Security Implications
Transport Layer Security: HTTPS encrypts the entire request/response cycle using TLS, preventing eavesdropping and tampering. Ruby's Net::HTTP enables HTTPS by setting use_ssl to true. Certificate verification should always be enabled in production to prevent man-in-the-middle attacks. Custom certificate stores can be configured for self-signed certificates in development.
require 'net/http'
uri = URI('https://api.example.com/data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# Enable certificate verification (default in modern Ruby)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
# Custom certificate store for self-signed certs (development only)
# http.cert_store = OpenSSL::X509::Store.new
# http.cert_store.add_file('/path/to/cert.pem')
response = http.get(uri.path)
Authentication Mechanisms: HTTP supports multiple authentication schemes. Basic authentication transmits credentials base64-encoded in the Authorization header, requiring HTTPS for security. Bearer tokens provide stateless authentication for APIs. OAuth 2.0 implements token-based authorization with refresh capabilities.
require 'net/http'
require 'base64'
# Basic authentication
uri = URI('https://api.example.com/secure')
request = Net::HTTP::Get.new(uri)
credentials = Base64.strict_encode64('username:password')
request['Authorization'] = "Basic #{credentials}"
# Bearer token authentication
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer eyJhbGciOiJIUzI1NiIs..."
# API key authentication
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = 'secret_key_here'
Cookie Security: Cookies maintain session state across requests. The Secure flag restricts cookies to HTTPS connections. The HttpOnly flag prevents JavaScript access, mitigating XSS attacks. SameSite attributes control cross-site request behavior, preventing CSRF attacks. Ruby automatically handles cookie storage and transmission.
Header Injection Vulnerabilities: Applications constructing HTTP requests from user input risk header injection attacks. Attackers insert newline characters to inject arbitrary headers or split the request. Always validate and sanitize user input before including it in headers.
# Vulnerable code - DO NOT USE
user_input = params[:user_agent]
request['User-Agent'] = user_input # Dangerous if user_input contains \r\n
# Secure approach - validate and sanitize
def sanitize_header(value)
value.gsub(/[\r\n]/, '')
end
request['User-Agent'] = sanitize_header(params[:user_agent])
Server-Side Request Forgery (SSRF): Applications that fetch URLs provided by users may be exploited to access internal resources or perform unauthorized requests. Always validate URLs against a whitelist and block requests to private IP ranges.
require 'net/http'
require 'ipaddr'
def safe_fetch(url_string)
uri = URI(url_string)
# Validate scheme
unless ['http', 'https'].include?(uri.scheme)
raise "Invalid URL scheme"
end
# Resolve hostname to IP
ip = IPSocket.getaddress(uri.host)
# Block private IP ranges
private_ranges = [
IPAddr.new('10.0.0.0/8'),
IPAddr.new('172.16.0.0/12'),
IPAddr.new('192.168.0.0/16'),
IPAddr.new('127.0.0.0/8')
]
if private_ranges.any? { |range| range.include?(ip) }
raise "Access to private IP denied"
end
Net::HTTP.get(uri)
rescue SocketError
raise "Invalid hostname"
end
Cross-Origin Resource Sharing (CORS): Browsers enforce same-origin policy, blocking JavaScript from making requests to different origins. Servers use CORS headers to explicitly allow cross-origin requests. Overly permissive CORS configurations create security vulnerabilities.
TLS Version and Cipher Selection: Older TLS versions and weak cipher suites have known vulnerabilities. Configure HTTP clients to use TLS 1.2 or higher and strong cipher suites.
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.min_version = OpenSSL::SSL::TLS1_2_VERSION
http.ciphers = 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'
Performance Considerations
Connection Reuse: Establishing TCP connections involves significant overhead—DNS resolution, TCP handshake, and TLS handshake for HTTPS. HTTP/1.1 persistent connections reuse TCP connections for multiple requests. Configure keep-alive timeouts appropriately to balance connection reuse with server resource consumption.
require 'net/http'
uri = URI('https://api.example.com')
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
# All requests reuse the same connection
10.times do |i|
response = http.get("/users/#{i}")
puts response.code
end
end # Connection automatically closed
Timeout Configuration: HTTP requests can hang indefinitely without timeouts. Configure separate timeouts for connection establishment and data reading. Connection timeouts prevent hanging on unreachable hosts. Read timeouts prevent hanging on slow responses.
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 5 # 5 seconds to establish connection
http.read_timeout = 30 # 30 seconds to read response
http.write_timeout = 10 # 10 seconds to write request body
Response Buffering: By default, HTTP clients buffer entire response bodies in memory. For large responses, streaming prevents memory exhaustion.
Compression: HTTP supports response body compression via Content-Encoding. Clients indicate compression support through Accept-Encoding headers. Gzip compression typically reduces transfer size by 60-80% for text content.
require 'net/http'
require 'zlib'
uri = URI('https://api.example.com/data')
request = Net::HTTP::Get.new(uri)
request['Accept-Encoding'] = 'gzip'
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.request(request)
body = if response['Content-Encoding'] == 'gzip'
Zlib::GzipReader.new(StringIO.new(response.body)).read
else
response.body
end
DNS Caching: DNS lookups add latency to each request. Operating systems cache DNS results, but applications can implement additional caching for frequently accessed hosts. Connection pooling implicitly caches DNS results by reusing connections.
HTTP/2 Multiplexing: HTTP/2 sends multiple requests over a single connection simultaneously, eliminating head-of-line blocking. Ruby's Net::HTTP does not support HTTP/2; use gems like httpx for HTTP/2 support.
Request Batching: APIs that support batch operations reduce network overhead. Instead of 100 individual requests, send one request with 100 operations.
Conditional Requests for Caching: ETags and Last-Modified headers enable conditional requests. Clients store these values and send If-None-Match or If-Modified-Since headers in subsequent requests. Servers respond with 304 Not Modified if the resource hasn't changed, saving bandwidth.
require 'net/http'
uri = URI('https://api.example.com/resource')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# Initial request
response = http.get(uri.path)
etag = response['ETag']
data = response.body
# Subsequent request with conditional header
request = Net::HTTP::Get.new(uri.path)
request['If-None-Match'] = etag
response = http.request(request)
if response.code == '304'
puts "Using cached data"
# Use previously stored data
else
data = response.body
etag = response['ETag']
end
Connection Pool Sizing: Connection pools reduce connection establishment overhead but consume server resources. Pool size should match expected concurrency. Too small causes contention; too large wastes resources.
Tools & Ecosystem
Faraday Middleware Ecosystem: Faraday's architecture supports extensive middleware for cross-cutting concerns. Popular middleware includes authentication handlers, retry logic, response parsing, caching, and instrumentation.
require 'faraday'
require 'faraday/retry'
require 'faraday_middleware'
conn = Faraday.new(url: 'https://api.example.com') do |f|
# Request middleware
f.request :oauth2, 'token123'
f.request :json
f.request :instrumentation
# Response middleware
f.response :json, content_type: /\bjson$/
f.response :follow_redirects
f.response :raise_error
f.use :instrumentation
# Retry with exponential backoff
f.request :retry,
max: 3,
interval: 0.5,
backoff_factor: 2,
retry_statuses: [429, 503]
f.adapter Faraday.default_adapter
end
REST Client Gem: Rest-client provides a simpler API than Net::HTTP for basic HTTP operations. It raises exceptions for error status codes and handles redirects automatically.
require 'rest-client'
# Simple GET request
response = RestClient.get(
'https://api.example.com/users/1',
{ Authorization: 'Bearer token123' }
)
puts response.body
# POST with error handling
begin
response = RestClient.post(
'https://api.example.com/users',
{ name: 'John', email: 'john@example.com' }.to_json,
{ content_type: :json, accept: :json }
)
rescue RestClient::ExceptionWithResponse => e
puts "Error: #{e.response}"
end
HTTP.rb Gem: The http gem provides a chainable API with sensible defaults and good performance characteristics.
require 'http'
response = HTTP
.timeout(10)
.headers(accept: 'application/json')
.auth("Bearer token123")
.get('https://api.example.com/users/1')
if response.status.success?
puts response.parse # Automatically parses JSON
end
WebMock for Testing: WebMock stubs HTTP requests in tests, eliminating external dependencies and improving test reliability and speed.
require 'webmock/rspec'
RSpec.describe 'API Client' do
before do
stub_request(:get, 'https://api.example.com/users/1')
.with(headers: { 'Authorization' => 'Bearer token123' })
.to_return(
status: 200,
body: { id: 1, name: 'John' }.to_json,
headers: { 'Content-Type' => 'application/json' }
)
end
it 'fetches user data' do
# Test code here
end
end
VCR for Record/Replay: VCR records HTTP interactions and replays them in tests, capturing real API responses while maintaining test speed and isolation.
require 'vcr'
VCR.configure do |c|
c.cassette_library_dir = 'spec/vcr_cassettes'
c.hook_into :webmock
c.filter_sensitive_data('<TOKEN>') { ENV['API_TOKEN'] }
end
RSpec.describe 'GitHub API' do
it 'fetches user', :vcr do
# First run records actual API call
# Subsequent runs replay from cassette
response = client.get_user('octocat')
expect(response['login']).to eq('octocat')
end
end
HTTPie for Command Line Testing: While not Ruby-specific, httpie provides an intuitive CLI for testing HTTP APIs during development.
Rack::Test for Application Testing: Rack::Test simulates HTTP requests to Rack applications without network overhead, ideal for integration testing web applications.
require 'rack/test'
class AppTest < Minitest::Test
include Rack::Test::Methods
def app
MyRackApp.new
end
def test_get_user
get '/users/1', {}, { 'HTTP_AUTHORIZATION' => 'Bearer token' }
assert last_response.ok?
assert_equal 'application/json', last_response.content_type
end
end
Reference
HTTP Request Methods
| Method | Purpose | Idempotent | Request Body |
|---|---|---|---|
| GET | Retrieve resource | Yes | No |
| POST | Create resource | No | Yes |
| PUT | Replace resource | Yes | Yes |
| PATCH | Partial update | No | Yes |
| DELETE | Remove resource | Yes | Optional |
| HEAD | Retrieve headers only | Yes | No |
| OPTIONS | Describe options | Yes | No |
| TRACE | Echo request | Yes | No |
HTTP Status Code Categories
| Range | Category | Description | Common Codes |
|---|---|---|---|
| 1xx | Informational | Request received, continuing | 100 Continue, 101 Switching Protocols |
| 2xx | Success | Request successfully processed | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection | Further action required | 301 Moved Permanently, 302 Found, 304 Not Modified |
| 4xx | Client Error | Request contains error | 400 Bad Request, 401 Unauthorized, 404 Not Found, 429 Too Many Requests |
| 5xx | Server Error | Server failed to fulfill request | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
Common Request Headers
| Header | Purpose | Example |
|---|---|---|
| Accept | Acceptable response formats | application/json |
| Authorization | Authentication credentials | Bearer token123 |
| Content-Type | Request body format | application/json |
| User-Agent | Client identification | Ruby/3.0 |
| Accept-Encoding | Supported compression | gzip, deflate |
| If-None-Match | Conditional request by ETag | "abc123" |
| If-Modified-Since | Conditional request by date | Sat, 29 Oct 2024 19:43:31 GMT |
| Cookie | Session state | session_id=xyz789 |
Common Response Headers
| Header | Purpose | Example |
|---|---|---|
| Content-Type | Response body format | application/json; charset=utf-8 |
| Content-Length | Response body size in bytes | 1234 |
| Content-Encoding | Compression applied | gzip |
| Cache-Control | Caching directives | max-age=3600, public |
| ETag | Resource version identifier | "abc123" |
| Last-Modified | Resource modification time | Sat, 29 Oct 2024 19:43:31 GMT |
| Location | Redirect target | https://example.com/new-location |
| Set-Cookie | Session state | session_id=xyz789; Secure; HttpOnly |
| Access-Control-Allow-Origin | CORS policy | https://example.com |
Ruby HTTP Client Library Comparison
| Library | Standard Library | API Style | HTTP/2 Support | Async Support |
|---|---|---|---|---|
| Net::HTTP | Yes | Imperative | No | No |
| HTTParty | No | Declarative | No | No |
| Faraday | No | Middleware-based | Via adapter | Via adapter |
| rest-client | No | Method-based | No | No |
| http.rb | No | Chainable | No | No |
| httpx | No | Chainable | Yes | Yes |
| async-http | No | Fiber-based | Yes | Yes |
Timeout Configuration Recommendations
| Timeout Type | Typical Range | Purpose |
|---|---|---|
| Open Timeout | 2-10 seconds | Connection establishment |
| Read Timeout | 10-60 seconds | Reading response data |
| Write Timeout | 5-30 seconds | Writing request data |
| Keep-Alive Timeout | 60-300 seconds | Persistent connection idle time |
Net::HTTP Core Classes
| Class | Purpose |
|---|---|
| Net::HTTP | HTTP client connection manager |
| Net::HTTP::Get | GET request object |
| Net::HTTP::Post | POST request object |
| Net::HTTP::Put | PUT request object |
| Net::HTTP::Delete | DELETE request object |
| Net::HTTP::Patch | PATCH request object |
| Net::HTTPResponse | Response base class |
| Net::HTTPSuccess | 2xx response category |
| Net::HTTPRedirection | 3xx response category |
| Net::HTTPClientError | 4xx response category |
| Net::HTTPServerError | 5xx response category |
Security Header Recommendations
| Header | Recommended Value | Purpose |
|---|---|---|
| Strict-Transport-Security | max-age=31536000; includeSubDomains | Force HTTPS |
| X-Content-Type-Options | nosniff | Prevent MIME sniffing |
| X-Frame-Options | DENY | Prevent clickjacking |
| Content-Security-Policy | default-src 'self' | Restrict resource loading |
| X-XSS-Protection | 1; mode=block | Enable XSS filtering |