CrackedRuby logo

CrackedRuby

Net::HTTP

Overview

Net::HTTP provides Ruby's standard HTTP client implementation for communicating with web servers and APIs. The library handles HTTP/1.1 protocol operations including request methods, response processing, connection management, and SSL/TLS encryption. Ruby includes Net::HTTP in its standard library, making it available without external dependencies.

The primary class Net::HTTP manages connections to HTTP servers, while request classes like Net::HTTP::Get and Net::HTTP::Post represent specific HTTP methods. Response objects inherit from Net::HTTPResponse and provide access to status codes, headers, and body content. The library supports persistent connections, chunked transfer encoding, and various authentication mechanisms.

require 'net/http'

# Basic GET request
uri = URI('https://api.example.com/users')
response = Net::HTTP.get_response(uri)
puts response.code        # => "200"
puts response.body        # => JSON response

Net::HTTP operates through three main patterns: convenience methods for simple requests, instance-based connections for multiple requests, and start blocks for fine-grained control. The convenience methods handle connection lifecycle automatically, while instance methods provide control over connection reuse and configuration.

# Instance-based approach
http = Net::HTTP.new('api.example.com', 443)
http.use_ssl = true
response = http.get('/users/1')

The library integrates with Ruby's URI class for URL parsing and supports both block-based and direct response handling. Request customization occurs through headers, request bodies, and connection parameters. Net::HTTP handles common HTTP features including redirects, cookies through manual header management, and content encoding.

Basic Usage

Net::HTTP supports multiple request methods through dedicated classes and convenience methods. GET requests retrieve data from servers, while POST requests send data for processing. PUT and DELETE methods handle resource updates and removal respectively.

require 'net/http'
require 'uri'

# GET request with URI parsing
uri = URI('https://jsonplaceholder.typicode.com/posts/1')
response = Net::HTTP.get_response(uri)

if response.is_a?(Net::HTTPSuccess)
  puts response.body
else
  puts "Request failed: #{response.code}"
end

POST requests require request body data and appropriate headers. The library supports form data, JSON payloads, and custom content types. Headers set through the request object control server interpretation of the request body.

# POST request with JSON data
require 'json'

uri = URI('https://jsonplaceholder.typicode.com/posts')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = {
  title: 'New Post',
  body: 'Post content here',
  userId: 1
}.to_json

response = http.request(request)
puts response.code
puts response.body

Form data submissions use URL-encoded format or multipart encoding. Net::HTTP handles URL encoding through the URI module, while form data requires manual construction or external libraries for complex multipart data.

# Form data POST
uri = URI('https://httpbin.org/post')
response = Net::HTTP.post_form(uri, {
  'field1' => 'value1',
  'field2' => 'value2'
})

puts response.body

Persistent connections improve performance for multiple requests to the same server. The start block ensures proper connection closure, while the instance approach requires manual connection management.

# Persistent connection with start block
Net::HTTP.start('api.example.com', 443, use_ssl: true) do |http|
  # Multiple requests share the same connection
  response1 = http.get('/users/1')
  response2 = http.get('/users/2')
  response3 = http.post('/users', 'name=John&email=john@example.com')
end

Request headers customize server behavior and client identification. Common headers include Accept for content negotiation, Authorization for authentication, and User-Agent for client identification.

# Custom headers and authentication
uri = URI('https://api.github.com/user')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer your-token-here'
request['Accept'] = 'application/vnd.github.v3+json'
request['User-Agent'] = 'MyApp/1.0'

response = http.request(request)

Error Handling & Debugging

Net::HTTP raises specific exceptions for different failure conditions. Network-level problems generate Errno exceptions, while HTTP-level issues create Net::HTTP exceptions. Timeout conditions raise Net::TimeoutError, and SSL problems generate OpenSSL::SSL::SSLError.

require 'net/http'
require 'timeout'

def robust_http_request(uri)
  begin
    response = Net::HTTP.get_response(uri)
    
    case response
    when Net::HTTPSuccess
      response.body
    when Net::HTTPRedirection
      handle_redirect(response)
    when Net::HTTPClientError
      raise "Client error: #{response.code} #{response.message}"
    when Net::HTTPServerError
      raise "Server error: #{response.code} #{response.message}"
    end
    
  rescue Net::TimeoutError => e
    raise "Request timeout: #{e.message}"
  rescue Net::HTTPError => e
    raise "HTTP error: #{e.message}"
  rescue Errno::ECONNREFUSED => e
    raise "Connection refused: #{e.message}"
  rescue Errno::EHOSTUNREACH => e
    raise "Host unreachable: #{e.message}"
  rescue OpenSSL::SSL::SSLError => e
    raise "SSL error: #{e.message}"
  rescue StandardError => e
    raise "Unexpected error: #{e.message}"
  end
end

Response validation prevents processing of invalid or unexpected responses. Status code checking determines successful requests, while header validation ensures expected content types and encoding.

def validate_response(response)
  # Check status code ranges
  unless (200..299).include?(response.code.to_i)
    raise "HTTP #{response.code}: #{response.message}"
  end
  
  # Validate content type
  content_type = response['Content-Type']
  unless content_type&.include?('application/json')
    raise "Unexpected content type: #{content_type}"
  end
  
  # Check required headers
  if response['Content-Length'].nil? && response['Transfer-Encoding'] != 'chunked'
    warn "Response missing content length information"
  end
  
  response
end

Connection debugging involves examining network traffic and connection states. Ruby's logging capabilities help track request progression and identify bottlenecks or failures.

# Enable debug output
http = Net::HTTP.new('example.com', 443)
http.use_ssl = true
http.set_debug_output($stdout)  # Prints HTTP traffic

# Custom debugging with logging
require 'logger'

logger = Logger.new('http_debug.log')

def debug_request(uri, logger)
  start_time = Time.now
  
  begin
    response = Net::HTTP.get_response(uri)
    duration = Time.now - start_time
    
    logger.info "Request: #{uri}"
    logger.info "Status: #{response.code}"
    logger.info "Duration: #{duration.round(3)}s"
    logger.info "Response size: #{response.body.length} bytes"
    
    response
  rescue => e
    logger.error "Request failed: #{e.message}"
    logger.error "Duration: #{(Time.now - start_time).round(3)}s"
    raise
  end
end

SSL certificate validation requires proper configuration to avoid security vulnerabilities. Development environments often disable verification, but production code must validate certificates properly.

# SSL configuration and validation
uri = URI('https://example.com')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

# Production: verify certificates (default)
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.ca_file = '/path/to/ca-bundle.crt'  # System CA bundle

# Custom certificate validation
http.verify_callback = proc do |preverify_ok, ssl_context|
  if preverify_ok
    true
  else
    cert = ssl_context.current_cert
    puts "SSL Certificate verification failed: #{cert.subject}"
    false  # Reject invalid certificates
  end
end

Production Patterns

Production deployment of Net::HTTP requires timeout configuration to prevent indefinite blocking. Connection timeouts control initial connection establishment, while read timeouts limit response waiting periods. Write timeouts prevent hanging during request body transmission.

class HttpClient
  def initialize(base_url)
    @uri = URI(base_url)
    @http = Net::HTTP.new(@uri.host, @uri.port)
    
    if @uri.scheme == 'https'
      @http.use_ssl = true
      @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
    end
    
    # Production timeout settings
    @http.open_timeout = 10      # Connection establishment
    @http.read_timeout = 30      # Response reading  
    @http.write_timeout = 10     # Request body writing
    @http.ssl_timeout = 10       # SSL handshake
  end
  
  def get(path, headers = {})
    request = Net::HTTP::Get.new(path)
    headers.each { |key, value| request[key] = value }
    
    execute_request(request)
  end
  
  private
  
  def execute_request(request)
    retries = 0
    begin
      @http.request(request)
    rescue Net::TimeoutError, Errno::ECONNRESET => e
      retries += 1
      if retries <= 3
        sleep(0.5 * retries)  # Exponential backoff
        retry
      else
        raise e
      end
    end
  end
end

Connection pooling improves performance by reusing established connections. Ruby's Net::HTTP supports persistent connections within start blocks, but application-level pooling provides better resource management across multiple requests.

class ConnectionPool
  def initialize(host, port, pool_size: 5)
    @host = host
    @port = port
    @pool_size = pool_size
    @pool = Queue.new
    @created = 0
    @mutex = Mutex.new
  end
  
  def with_connection
    connection = acquire_connection
    begin
      yield connection
    ensure
      release_connection(connection)
    end
  end
  
  private
  
  def acquire_connection
    if @pool.empty? && @created < @pool_size
      @mutex.synchronize do
        if @created < @pool_size
          @created += 1
          return create_connection
        end
      end
    end
    
    @pool.pop  # Blocks until connection available
  end
  
  def release_connection(connection)
    @pool.push(connection) if connection_valid?(connection)
  end
  
  def create_connection
    http = Net::HTTP.new(@host, @port)
    http.use_ssl = (@port == 443)
    http.start
    http
  end
  
  def connection_valid?(connection)
    connection.started? && !connection.finished?
  rescue
    false
  end
end

Monitoring and observability require request tracking, performance measurement, and error aggregation. Production applications instrument HTTP requests to identify slow endpoints and connection problems.

module HttpInstrumentation
  def self.instrument_request(uri, &block)
    start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    request_id = SecureRandom.uuid
    
    begin
      Rails.logger.info "HTTP_REQUEST_START", {
        request_id: request_id,
        uri: uri.to_s,
        timestamp: Time.current.iso8601
      }
      
      result = yield
      
      duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
      
      Rails.logger.info "HTTP_REQUEST_COMPLETE", {
        request_id: request_id,
        status: result.code,
        duration_ms: (duration * 1000).round(2),
        response_size: result.body.length
      }
      
      # Send metrics to monitoring system
      StatsD.histogram('http_client.request_duration', duration * 1000, tags: [
        "host:#{uri.host}",
        "status:#{result.code}"
      ])
      
      result
      
    rescue => error
      duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
      
      Rails.logger.error "HTTP_REQUEST_ERROR", {
        request_id: request_id,
        error_class: error.class.name,
        error_message: error.message,
        duration_ms: (duration * 1000).round(2)
      }
      
      StatsD.increment('http_client.request_error', tags: [
        "host:#{uri.host}",
        "error:#{error.class.name}"
      ])
      
      raise error
    end
  end
end

Circuit breaker patterns protect against cascading failures by temporarily disabling requests to failing services. This prevents resource exhaustion and allows failing services time to recover.

class CircuitBreaker
  STATES = [:closed, :open, :half_open].freeze
  
  def initialize(failure_threshold: 5, recovery_timeout: 30)
    @failure_threshold = failure_threshold
    @recovery_timeout = recovery_timeout
    @failure_count = 0
    @last_failure_time = nil
    @state = :closed
    @mutex = Mutex.new
  end
  
  def call
    case @state
    when :closed
      execute_request { yield }
    when :open
      if time_to_attempt_reset?
        attempt_reset { yield }
      else
        raise CircuitBreakerOpenError, "Circuit breaker is open"
      end
    when :half_open
      execute_request { yield }
    end
  end
  
  private
  
  def execute_request
    begin
      result = yield
      on_success
      result
    rescue => error
      on_failure
      raise error
    end
  end
  
  def on_success
    @mutex.synchronize do
      @failure_count = 0
      @state = :closed
    end
  end
  
  def on_failure
    @mutex.synchronize do
      @failure_count += 1
      @last_failure_time = Time.current
      
      if @failure_count >= @failure_threshold
        @state = :open
      end
    end
  end
  
  def time_to_attempt_reset?
    Time.current - @last_failure_time > @recovery_timeout
  end
  
  def attempt_reset
    @mutex.synchronize { @state = :half_open }
    execute_request { yield }
  end
end

class CircuitBreakerOpenError < StandardError; end

Reference

Core Classes

Class Description
Net::HTTP Main HTTP client class for connection management
Net::HTTP::Get GET request representation
Net::HTTP::Post POST request representation
Net::HTTP::Put PUT request representation
Net::HTTP::Delete DELETE request representation
Net::HTTP::Head HEAD request representation
Net::HTTP::Options OPTIONS request representation
Net::HTTP::Patch PATCH request representation

Net::HTTP Instance Methods

Method Parameters Returns Description
#new(address, port) address (String), port (Integer) Net::HTTP Creates new HTTP connection
#start(&block) Block (optional) Net::HTTP or block result Opens connection, yields to block if given
#finish None Net::HTTP Closes connection
#started? None Boolean Connection status check
#use_ssl=(flag) flag (Boolean) Boolean Enables/disables SSL
#request(request) request (Net::HTTPRequest) Net::HTTPResponse Executes HTTP request
#get(path, headers) path (String), headers (Hash) Net::HTTPResponse Performs GET request
#post(path, data, headers) path (String), data (String), headers (Hash) Net::HTTPResponse Performs POST request

Timeout Configuration

Property Type Default Description
open_timeout Integer 60 Connection establishment timeout (seconds)
read_timeout Integer 60 Response reading timeout (seconds)
write_timeout Integer 60 Request writing timeout (seconds)
ssl_timeout Integer nil SSL handshake timeout (seconds)
continue_timeout Integer nil 100 Continue response timeout (seconds)
keep_alive_timeout Integer 2 Keep-alive probe timeout (seconds)

SSL Configuration

Property Type Description
use_ssl Boolean Enable SSL/TLS connection
verify_mode Integer Certificate verification mode
ca_file String Path to CA certificate file
ca_path String Path to CA certificate directory
cert OpenSSL::X509::Certificate Client certificate
key OpenSSL::PKey Client private key
ssl_version Symbol SSL/TLS protocol version
ciphers String/Array Allowed cipher suites

Response Classes

Class Status Range Description
Net::HTTPSuccess 200-299 Successful responses
Net::HTTPRedirection 300-399 Redirection responses
Net::HTTPClientError 400-499 Client error responses
Net::HTTPServerError 500-599 Server error responses
Net::HTTPInformation 100-199 Informational responses

Common Response Status Codes

Code Class Constant
200 Net::HTTPOK Net::HTTP::OK
201 Net::HTTPCreated Net::HTTP::Created
301 Net::HTTPMovedPermanently Net::HTTP::MovedPermanently
302 Net::HTTPFound Net::HTTP::Found
400 Net::HTTPBadRequest Net::HTTP::BadRequest
401 Net::HTTPUnauthorized Net::HTTP::Unauthorized
404 Net::HTTPNotFound Net::HTTP::NotFound
500 Net::HTTPInternalServerError Net::HTTP::InternalServerError

Exception Hierarchy

StandardError
├── Net::HTTPError
│   ├── Net::HTTPClientException
│   ├── Net::HTTPFatalError
│   └── Net::HTTPRetriableError
├── Net::TimeoutError
├── Errno::ECONNREFUSED
├── Errno::EHOSTUNREACH
└── OpenSSL::SSL::SSLError

Convenience Methods

Method Parameters Returns Description
Net::HTTP.get(uri) uri (URI/String) String GET request body
Net::HTTP.get_response(uri) uri (URI/String) Net::HTTPResponse GET request response
Net::HTTP.post_form(uri, params) uri (URI), params (Hash) Net::HTTPResponse POST form data
Net::HTTP.start(host, port, &block) host (String), port (Integer), block Block result Connection with start block

Authentication Methods

Type Header Format Example
Basic Authorization: Basic <base64> request['Authorization'] = 'Basic ' + ['user:pass'].pack('m0')
Bearer Authorization: Bearer <token> request['Authorization'] = 'Bearer token123'
Digest Authorization: Digest <params> Calculated using challenge response
API Key X-API-Key: <key> request['X-API-Key'] = 'your-api-key'

Content-Type Headers

Type Header Value Usage
JSON application/json API requests with JSON payloads
Form application/x-www-form-urlencoded HTML form submissions
Multipart multipart/form-data File uploads with forms
XML application/xml XML document transmission
Plain Text text/plain Simple text content
Binary application/octet-stream Binary file transfers