CrackedRuby logo

CrackedRuby

WEBrick Server

Overview

WEBrick provides Ruby's standard library HTTP server implementation. The library includes WEBrick::HTTPServer as the primary server class, along with supporting classes for request handling, response generation, and servlet management. WEBrick operates as a pure Ruby solution, making it platform-independent and easy to embed within Ruby applications.

The server architecture centers around a main server instance that listens on a specified port and dispatches incoming requests to registered servlets. Each servlet handles specific URL patterns and generates appropriate HTTP responses. WEBrick supports both single-threaded and multi-threaded operation modes, with configurable thread pools for concurrent request handling.

WEBrick handles the complete HTTP protocol implementation, including request parsing, header processing, status code management, and response formatting. The library automatically manages connection lifecycle, request routing, and error response generation.

require 'webrick'

# Basic server creation
server = WEBrick::HTTPServer.new(Port: 8000)

# Mount a simple servlet
server.mount_proc('/hello') do |req, res|
  res.body = 'Hello, World!'
end

# Start the server
server.start
require 'webrick'

# Server with custom document root
server = WEBrick::HTTPServer.new(
  Port: 8000,
  DocumentRoot: './public'
)

# Serve static files from public directory
server.start

The WEBrick::HTTPServer class accepts configuration options through its constructor, including port numbers, document roots, access logs, and security settings. The server automatically handles static file serving when a document root is specified, while custom servlets handle dynamic content generation.

Basic Usage

WEBrick servers require explicit configuration and servlet mounting before starting. The WEBrick::HTTPServer.new method accepts a configuration hash specifying operational parameters. Common configuration options include :Port, :Host, :DocumentRoot, and :AccessLog.

require 'webrick'

server = WEBrick::HTTPServer.new(
  Port: 3000,
  Host: '127.0.0.1',
  AccessLog: [[STDOUT, WEBrick::AccessLog::COMMON_LOG_FORMAT]]
)

# Mount a dynamic servlet
server.mount_proc('/time') do |request, response|
  response.content_type = 'text/plain'
  response.body = Time.now.to_s
end

# Mount static file serving
server.mount('/', WEBrick::HTTPServlet::FileHandler, './public')

server.start

The mount_proc method attaches block-based request handlers to specific URL paths. The block receives WEBrick::HTTPRequest and WEBrick::HTTPResponse objects, allowing full control over request processing and response generation. Request objects provide access to HTTP methods, headers, parameters, and body content.

require 'webrick'

server = WEBrick::HTTPServer.new(Port: 8080)

server.mount_proc('/form') do |req, res|
  if req.request_method == 'GET'
    res.content_type = 'text/html'
    res.body = '<form method="post"><input name="message"><button>Send</button></form>'
  elsif req.request_method == 'POST'
    message = req.query['message']
    res.content_type = 'text/plain'
    res.body = "Received: #{message}"
  end
end

server.start

Response objects support setting HTTP status codes, headers, and body content. The content_type attribute sets the Content-Type header, while the body attribute contains the response content. WEBrick automatically calculates content length and handles header formatting.

require 'webrick'

server = WEBrick::HTTPServer.new(Port: 8080)

server.mount_proc('/api/data') do |req, res|
  res.status = 200
  res.content_type = 'application/json'
  res['Cache-Control'] = 'no-cache'
  
  data = { timestamp: Time.now.to_i, status: 'ok' }
  res.body = JSON.generate(data)
end

server.start

The mount method attaches servlet classes to URL paths. WEBrick includes built-in servlet classes like WEBrick::HTTPServlet::FileHandler for static file serving and WEBrick::HTTPServlet::CGIHandler for CGI script execution. Custom servlet classes inherit from WEBrick::HTTPServlet::AbstractServlet and implement HTTP method handlers.

Advanced Usage

WEBrick supports custom servlet classes for complex request handling logic. Servlet classes inherit from WEBrick::HTTPServlet::AbstractServlet and implement methods corresponding to HTTP verbs: do_GET, do_POST, do_PUT, and do_DELETE. Each method receives request and response objects as parameters.

require 'webrick'

class APIServlet < WEBrick::HTTPServlet::AbstractServlet
  def initialize(server, data_store)
    super(server)
    @data_store = data_store
  end
  
  def do_GET(request, response)
    id = request.query['id']
    if id && @data_store[id]
      response.status = 200
      response.content_type = 'application/json'
      response.body = JSON.generate(@data_store[id])
    else
      response.status = 404
      response.body = 'Not Found'
    end
  end
  
  def do_POST(request, response)
    data = JSON.parse(request.body)
    id = SecureRandom.uuid
    @data_store[id] = data.merge('id' => id)
    
    response.status = 201
    response.content_type = 'application/json'
    response.body = JSON.generate(@data_store[id])
  end
end

data_store = {}
server = WEBrick::HTTPServer.new(Port: 8080)
server.mount('/api', APIServlet, data_store)
server.start

WEBrick configuration accepts detailed server options through the configuration hash. The :Logger option specifies logging behavior, :MaxClients controls concurrent connection limits, and :RequestTimeout sets request processing timeouts. SSL/TLS support requires :SSLEnable, :SSLCertificate, and :SSLPrivateKey options.

require 'webrick'
require 'webrick/https'
require 'openssl'

config = {
  Port: 8443,
  SSLEnable: true,
  SSLCertificate: OpenSSL::X509::Certificate.new(File.read('server.crt')),
  SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read('server.key')),
  SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
  MaxClients: 100,
  RequestTimeout: 30,
  Logger: WEBrick::Log.new('server.log', WEBrick::Log::INFO)
}

server = WEBrick::HTTPServer.new(config)
server.mount_proc('/secure') do |req, res|
  res.body = "Secure connection established"
end

server.start

The WEBrick::HTTPAuth module provides HTTP authentication mechanisms including Basic and Digest authentication. Authentication requires configuring user databases, password stores, and authentication handlers. The authentication system integrates with servlet mounting to protect specific URL paths.

require 'webrick'

# Create password database
htpasswd = WEBrick::HTTPAuth::Htpasswd.new('users.htpasswd')
htpasswd.auth_type = WEBrick::HTTPAuth::BasicAuth
htpasswd.set_passwd('admin', 'admin', 'secret123')
htpasswd.set_passwd('admin', 'user', 'password456')

# Configure authentication
auth = WEBrick::HTTPAuth::BasicAuth.new(
  Realm: 'Admin Area',
  UserDB: htpasswd
)

server = WEBrick::HTTPServer.new(Port: 8080)

server.mount_proc('/admin') do |req, res|
  auth.authenticate(req, res)
  res.body = 'Admin panel access granted'
end

server.start

WEBrick supports virtual hosting through the WEBrick::HTTPServer::MountTable class. Virtual hosts allow different servlet configurations based on the Host header value. Each virtual host maintains separate servlet mountings and configuration options, enabling multi-domain server setups.

Error Handling & Debugging

WEBrick generates specific exception types for different error conditions. WEBrick::HTTPStatus::Error serves as the base class for HTTP status exceptions. Subclasses like WEBrick::HTTPStatus::NotFound and WEBrick::HTTPStatus::InternalServerError represent specific HTTP status codes. Servlets can raise these exceptions to generate appropriate error responses.

require 'webrick'

class SafeServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(request, response)
    begin
      resource_id = request.query['id']
      raise WEBrick::HTTPStatus::BadRequest, "Missing id parameter" unless resource_id
      
      # Simulate resource lookup
      if resource_id.to_i > 1000
        raise WEBrick::HTTPStatus::NotFound, "Resource not found"
      end
      
      response.content_type = 'application/json'
      response.body = JSON.generate({ id: resource_id, data: 'example' })
      
    rescue WEBrick::HTTPStatus::Status => e
      # WEBrick automatically handles HTTPStatus exceptions
      raise e
    rescue StandardError => e
      # Convert unexpected errors to 500 responses
      raise WEBrick::HTTPStatus::InternalServerError, e.message
    end
  end
end

server = WEBrick::HTTPServer.new(Port: 8080)
server.mount('/resource', SafeServlet)
server.start

WEBrick provides comprehensive logging through the WEBrick::Log class. The logger supports different log levels including DEBUG, INFO, WARN, ERROR, and FATAL. Access logs record HTTP requests using configurable formats, while error logs capture server exceptions and debugging information.

require 'webrick'

# Configure detailed logging
access_log = []
access_log << [STDOUT, WEBrick::AccessLog::COMBINED_LOG_FORMAT]
access_log << [File.open('access.log', 'w'), WEBrick::AccessLog::COMMON_LOG_FORMAT]

error_logger = WEBrick::Log.new('error.log', WEBrick::Log::DEBUG)

server = WEBrick::HTTPServer.new(
  Port: 8080,
  AccessLog: access_log,
  Logger: error_logger
)

server.mount_proc('/debug') do |req, res|
  server.logger.debug("Debug request received: #{req.request_uri}")
  server.logger.info("Request from: #{req.remote_ip}")
  
  res.body = 'Debug response'
  server.logger.debug("Response sent successfully")
end

server.start

Request validation requires explicit parameter checking and data sanitization. WEBrick does not provide built-in validation mechanisms, so servlets must implement appropriate checks for required parameters, data types, and security constraints. Input validation prevents common security vulnerabilities and improves error reporting.

require 'webrick'

class ValidatingServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_POST(request, response)
    begin
      # Validate Content-Type header
      content_type = request.content_type
      unless content_type&.include?('application/json')
        raise WEBrick::HTTPStatus::UnsupportedMediaType, 
              "Expected application/json, got #{content_type}"
      end
      
      # Parse and validate JSON body
      data = JSON.parse(request.body || '{}')
      
      required_fields = %w[name email]
      missing_fields = required_fields.select { |field| data[field].nil? || data[field].empty? }
      
      unless missing_fields.empty?
        raise WEBrick::HTTPStatus::BadRequest, 
              "Missing required fields: #{missing_fields.join(', ')}"
      end
      
      # Validate email format
      email_regex = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
      unless data['email'].match?(email_regex)
        raise WEBrick::HTTPStatus::BadRequest, "Invalid email format"
      end
      
      response.status = 200
      response.content_type = 'application/json'
      response.body = JSON.generate({ status: 'created', id: SecureRandom.uuid })
      
    rescue JSON::ParserError
      raise WEBrick::HTTPStatus::BadRequest, "Invalid JSON in request body"
    end
  end
end

Thread Safety & Concurrency

WEBrick operates in multi-threaded mode by default, creating new threads for each client connection. The :MaxClients configuration option limits concurrent connections, while :RequestTimeout prevents resource exhaustion from slow clients. Thread safety becomes critical when servlets access shared data structures or external resources.

require 'webrick'
require 'thread'

class ThreadSafeCounter < WEBrick::HTTPServlet::AbstractServlet
  def initialize(server)
    super(server)
    @counter = 0
    @mutex = Mutex.new
  end
  
  def do_GET(request, response)
    action = request.query['action']
    
    result = @mutex.synchronize do
      case action
      when 'increment'
        @counter += 1
      when 'decrement'
        @counter -= 1
      when 'get'
        @counter
      else
        raise WEBrick::HTTPStatus::BadRequest, "Invalid action: #{action}"
      end
    end
    
    response.content_type = 'application/json'
    response.body = JSON.generate({ counter: result, thread_id: Thread.current.object_id })
  end
end

server = WEBrick::HTTPServer.new(
  Port: 8080,
  MaxClients: 10,
  RequestTimeout: 30
)

server.mount('/counter', ThreadSafeCounter)
server.start

Connection pools and resource management require careful synchronization when servlets access databases, file systems, or external services. Ruby's ConnectionPool gem integrates well with WEBrick for managing shared resources across request threads.

require 'webrick'
require 'connection_pool'

class DatabaseServlet < WEBrick::HTTPServlet::AbstractServlet
  def initialize(server, db_pool)
    super(server)
    @db_pool = db_pool
  end
  
  def do_GET(request, response)
    user_id = request.query['user_id']
    
    @db_pool.with do |connection|
      # Simulate database query
      user_data = connection.execute("SELECT * FROM users WHERE id = ?", user_id)
      
      response.content_type = 'application/json'
      response.body = JSON.generate(user_data)
    end
  rescue => e
    server.logger.error("Database error: #{e.message}")
    raise WEBrick::HTTPStatus::InternalServerError, "Database unavailable"
  end
end

# Create connection pool (pseudo-code for database connections)
db_pool = ConnectionPool.new(size: 5, timeout: 5) do
  # Database connection creation logic
  MockDatabaseConnection.new
end

server = WEBrick::HTTPServer.new(Port: 8080)
server.mount('/users', DatabaseServlet, db_pool)
server.start

WEBrick supports single-threaded operation through custom server configurations. Single-threaded servers avoid synchronization overhead but cannot handle concurrent requests. This mode works well for development environments or applications with minimal concurrent load requirements.

require 'webrick'

# Custom single-threaded server
class SingleThreadServer < WEBrick::HTTPServer
  def start(&block)
    @listen_socket = TCPServer.new(@config[:BindAddress], @config[:Port])
    
    loop do
      begin
        sock = @listen_socket.accept
        req = WEBrick::HTTPRequest.new(@config)
        res = WEBrick::HTTPResponse.new(@config)
        
        req.parse(sock)
        service(req, res)
        res.send_response(sock)
        
      ensure
        sock&.close
      end
    end
  end
end

server = SingleThreadServer.new(Port: 8080)
server.mount_proc('/single') do |req, res|
  res.body = "Single-threaded response from #{Thread.current.object_id}"
end

server.start

Production Patterns

WEBrick deployment in production environments requires careful configuration of security settings, logging, and process management. While WEBrick handles small to medium traffic loads, high-traffic applications typically use WEBrick behind reverse proxies like nginx or Apache for static content serving and SSL termination.

require 'webrick'
require 'logger'

class ProductionServer
  def initialize(port, document_root)
    @config = {
      Port: port,
      DocumentRoot: document_root,
      DirectoryIndex: %w[index.html index.htm],
      AccessLog: setup_access_logging,
      Logger: setup_error_logging,
      MaxClients: 50,
      RequestTimeout: 60,
      ServerType: WEBrick::Daemon,
      ServerSoftware: "MyApp/1.0",
      DoNotReverseLookup: true
    }
    
    @server = WEBrick::HTTPServer.new(@config)
    setup_signal_handling
    mount_applications
  end
  
  private
  
  def setup_access_logging
    access_log = []
    
    # Console logging for development
    if ENV['RACK_ENV'] != 'production'
      access_log << [STDOUT, WEBrick::AccessLog::COMBINED_LOG_FORMAT]
    end
    
    # File logging
    log_file = File.open('log/access.log', 'a')
    access_log << [log_file, WEBrick::AccessLog::COMBINED_LOG_FORMAT]
    
    access_log
  end
  
  def setup_error_logging
    WEBrick::Log.new('log/error.log', WEBrick::Log::INFO)
  end
  
  def setup_signal_handling
    %w[INT TERM].each do |signal|
      Signal.trap(signal) { @server.shutdown }
    end
  end
  
  def mount_applications
    # Static file serving
    @server.mount('/', WEBrick::HTTPServlet::FileHandler, 'public')
    
    # API endpoints
    @server.mount('/api/health', HealthCheckServlet)
    @server.mount('/api/v1', APIServlet)
    
    # Error pages
    @server.mount('/404.html', WEBrick::HTTPServlet::FileHandler, 'public/404.html')
  end
  
  def start
    @server.start
  end
end

# Usage
server = ProductionServer.new(8080, 'public')
server.start

Health check endpoints enable monitoring systems to verify server availability and application status. These endpoints typically return JSON responses with system metrics, dependency status, and application version information.

require 'webrick'

class HealthCheckServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(request, response)
    health_data = {
      status: 'healthy',
      timestamp: Time.now.iso8601,
      version: '1.0.0',
      uptime: Process.clock_gettime(Process::CLOCK_MONOTONIC).to_i,
      checks: perform_health_checks
    }
    
    if health_data[:checks].any? { |check| check[:status] != 'ok' }
      response.status = 503
      health_data[:status] = 'unhealthy'
    else
      response.status = 200
    end
    
    response.content_type = 'application/json'
    response.body = JSON.generate(health_data)
  end
  
  private
  
  def perform_health_checks
    checks = []
    
    # Database connectivity
    checks << {
      name: 'database',
      status: database_connected? ? 'ok' : 'error',
      response_time_ms: measure_database_response_time
    }
    
    # External service availability
    checks << {
      name: 'external_api',
      status: external_api_available? ? 'ok' : 'error',
      response_time_ms: measure_api_response_time
    }
    
    # Disk space
    checks << {
      name: 'disk_space',
      status: sufficient_disk_space? ? 'ok' : 'warning',
      available_gb: available_disk_space_gb
    }
    
    checks
  end
end

Process management for production WEBrick servers requires implementing graceful shutdown handling, process monitoring, and restart mechanisms. Daemon mode operation detaches the server process from the controlling terminal, while proper signal handling ensures clean resource cleanup.

require 'webrick'

class ManagedServer
  def initialize
    @server = nil
    @running = false
    setup_signal_handlers
  end
  
  def start(daemonize: false)
    config = {
      Port: ENV['PORT'] || 8080,
      ServerType: daemonize ? WEBrick::Daemon : nil,
      PidFile: 'tmp/server.pid',
      Logger: WEBrick::Log.new('log/server.log', WEBrick::Log::INFO),
      AccessLog: [[File.open('log/access.log', 'a'), WEBrick::AccessLog::COMBINED_LOG_FORMAT]]
    }
    
    @server = WEBrick::HTTPServer.new(config)
    @running = true
    
    # Write PID file for process management
    File.write(config[:PidFile], Process.pid) unless daemonize
    
    mount_routes
    
    begin
      @server.start
    ensure
      cleanup
    end
  end
  
  def stop
    return unless @running
    
    @server&.shutdown
    @running = false
  end
  
  private
  
  def setup_signal_handlers
    %w[INT TERM].each do |signal|
      Signal.trap(signal) do
        puts "Received #{signal}, shutting down gracefully..."
        stop
      end
    end
    
    Signal.trap('USR1') do
      puts "Received USR1, reloading configuration..."
      reload_config
    end
  end
  
  def cleanup
    File.delete('tmp/server.pid') if File.exist?('tmp/server.pid')
    puts "Server stopped"
  end
  
  def reload_config
    # Implement configuration reloading logic
    @server.logger.info("Configuration reloaded")
  end
  
  def mount_routes
    @server.mount_proc('/') do |req, res|
      res.body = 'Server running'
    end
  end
end

# Usage
server = ManagedServer.new
server.start(daemonize: ARGV.include?('--daemon'))

Reference

WEBrick::HTTPServer Configuration Options

Option Type Default Description
:Port Integer 80 TCP port number for server binding
:Host String '0.0.0.0' IP address for server binding
:BindAddress String nil Alias for :Host option
:DocumentRoot String nil Directory path for static file serving
:DirectoryIndex Array ['index.html'] Default files for directory requests
:MaxClients Integer 100 Maximum concurrent client connections
:RequestTimeout Integer 30 Request processing timeout in seconds
:ServerType Class nil WEBrick::Daemon for background operation
:ServerSoftware String "WEBrick/#{VERSION}" Server identification string
:Logger WEBrick::Log BasicLog Error logging configuration
:AccessLog Array [] Access logging configuration
:SSLEnable Boolean false Enable SSL/TLS encryption
:SSLCertificate OpenSSL::X509::Certificate nil SSL certificate object
:SSLPrivateKey OpenSSL::PKey nil SSL private key object
:SSLVerifyClient Integer VERIFY_NONE SSL client verification mode

Request Object Methods

Method Returns Description
#request_method String HTTP method (GET, POST, PUT, DELETE)
#request_uri URI Complete request URI object
#path String URL path component
#query Hash Parsed query parameters
#body String Request body content
#content_type String Content-Type header value
#content_length Integer Content-Length header value
#remote_ip String Client IP address
#header Hash HTTP headers hash
#cookies Array Cookie objects array
#user String Authenticated username

Response Object Methods

Method Parameters Description
#status= Integer Set HTTP status code
#content_type= String Set Content-Type header
#body= String Set response body content
#[]=(key, value) String, String Set custom HTTP header
#cookies Cookie Access response cookies
#set_redirect String Set redirect location
#send_response IO Send response to output stream

Built-in Servlet Classes

Class Purpose Constructor Parameters
WEBrick::HTTPServlet::FileHandler Static file serving server, document_root, options
WEBrick::HTTPServlet::CGIHandler CGI script execution server, script_path
WEBrick::HTTPServlet::ERBHandler ERB template processing server, template_path
WEBrick::HTTPServlet::ProcHandler Proc-based request handling proc_object
WEBrick::HTTPServlet::DefaultFileHandler Directory index generation server, local_path

HTTP Status Exception Classes

Class Status Code Description
WEBrick::HTTPStatus::BadRequest 400 Malformed request syntax
WEBrick::HTTPStatus::Unauthorized 401 Authentication required
WEBrick::HTTPStatus::Forbidden 403 Access denied
WEBrick::HTTPStatus::NotFound 404 Resource not found
WEBrick::HTTPStatus::MethodNotAllowed 405 HTTP method not supported
WEBrick::HTTPStatus::InternalServerError 500 Server processing error
WEBrick::HTTPStatus::NotImplemented 501 Feature not implemented
WEBrick::HTTPStatus::BadGateway 502 Upstream server error
WEBrick::HTTPStatus::ServiceUnavailable 503 Service temporarily unavailable

Authentication Classes

Class Type Required Parameters
WEBrick::HTTPAuth::BasicAuth HTTP Basic :Realm, :UserDB
WEBrick::HTTPAuth::DigestAuth HTTP Digest :Realm, :UserDB, :Algorithm
WEBrick::HTTPAuth::Htpasswd Password file filename, password_hash
WEBrick::HTTPAuth::Htdigest Digest password file filename

Log Levels

Level Numeric Value Description
WEBrick::Log::FATAL 1 Fatal errors only
WEBrick::Log::ERROR 2 Error conditions
WEBrick::Log::WARN 3 Warning messages
WEBrick::Log::INFO 4 Informational messages
WEBrick::Log::DEBUG 5 Debug information

Common MIME Types

Extension MIME Type WEBrick Constant
.html text/html WEBrick::HTTPUtils::DefaultMimeTypes['.html']
.css text/css WEBrick::HTTPUtils::DefaultMimeTypes['.css']
.js application/javascript WEBrick::HTTPUtils::DefaultMimeTypes['.js']
.json application/json WEBrick::HTTPUtils::DefaultMimeTypes['.json']
.png image/png WEBrick::HTTPUtils::DefaultMimeTypes['.png']
.jpg image/jpeg WEBrick::HTTPUtils::DefaultMimeTypes['.jpg']