net/http
HTTP client and server package in Go standard library. Provides robustness, flexibility, and comprehensive documentation. Built-in HTTP/1.1, HTTP/2 support, detailed configuration options, cookie management, redirect handling, and timeout control. No external dependencies required.
GitHub Overview
ruby/ruby
The Ruby Programming Language
Topics
Star History
Library
Net::HTTP
Overview
Net::HTTP is "the HTTP client implementation in Ruby's standard library" developed as a foundational HTTP client library for the Ruby ecosystem. As part of Ruby's standard library, it has provided stable operation for many years, enabling HTTP communication through simple and intuitive APIs. Utilized in various use cases including RESTful API development, web scraping, and external service integration, it functions as the foundation for many Ruby HTTP libraries. With the high reliability and compatibility characteristic of standard libraries, it has established itself as an essential networking tool for Ruby developers.
Details
Net::HTTP 2025 edition is a mature HTTP client library with nearly 30 years of development experience as Ruby's standard library. While maintaining support for the latest features in Ruby 3.x series, it provides consistent operation across a wide range of Ruby versions through backward compatibility-focused design. It comprehensively supports everything from basic GET/POST/PUT/DELETE requests to advanced features such as SSL/TLS communication, proxy server support, cookie management, session management, form data transmission, and file upload. It provides both simple class method APIs and instance APIs that allow detailed control, enabling selection of optimal implementation methods according to use cases.
Key Features
- Ruby Standard Library: Pre-installed library requiring no additional installation
- Comprehensive HTTP Support: Support for GET, POST, PUT, DELETE, WebDAV, and other methods
- Complete SSL/TLS Support: HTTPS communication, certificate verification, encryption settings
- Session Management: Efficient connection pooling and persistent connections
- Rich Authentication Methods: Basic authentication, Digest authentication, proxy authentication
- Form Processing: Support for both multipart and URL encoded formats
Pros and Cons
Pros
- Available immediately without additional dependencies as Ruby's standard library
- Outstanding stability and maturity from nearly 30 years of development experience
- High performance and reliability through deep integration with Ruby language
- Low learning cost due to simple and intuitive API
- Capable of handling enterprise-level HTTP communication requirements with rich features
- High compatibility and ecosystem integration with other Ruby HTTP libraries
Cons
- Synchronization-based processing unsuitable for large numbers of parallel requests
- Requires dedicated libraries like em-http-request for asynchronous processing
- Some APIs are low-level, requiring more code compared to modern high-level libraries
- Convenience features like automatic JSON response parsing not provided by default
- Exception-based error handling requires detailed control
- Limited support for latest protocols like HTTP/2 and HTTP/3
Reference Pages
Code Examples
Installation and Basic Setup
# Only require needed as Ruby standard library
require 'net/http'
require 'uri'
require 'json'
require 'openssl'
# Explicit require may be needed in Ruby 3.0+
require 'net/http'
# Enable SSL certificate verification (recommended)
Net::HTTP.verify_mode = OpenSSL::SSL::VERIFY_PEER
# Global configuration (optional)
Net::HTTP.class_eval do
# Default timeout setting
@@default_timeout = 30
def self.default_timeout=(value)
@@default_timeout = value
end
def self.default_timeout
@@default_timeout
end
end
# Detailed log output for debugging (development only)
# Net::HTTP.start('example.com', 80, debug: true)
puts "Net::HTTP version: #{Net::HTTP::VERSION}"
puts "Ruby version: #{RUBY_VERSION}"
Basic HTTP Requests (GET/POST/PUT/DELETE)
require 'net/http'
require 'uri'
require 'json'
# Basic GET request
def simple_get_request
uri = URI('https://api.example.com/users')
begin
response = Net::HTTP.get_response(uri)
puts "Status: #{response.code} #{response.message}"
puts "Headers: #{response.to_hash}"
puts "Body: #{response.body}"
if response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
puts "Parsed JSON: #{data}"
return data
else
puts "Request failed: #{response.code}"
return nil
end
rescue StandardError => e
puts "Error: #{e.message}"
return nil
end
end
# GET request with query parameters
def get_request_with_params
base_uri = 'https://api.example.com/users'
params = {
page: 1,
limit: 20,
sort: 'created_at',
filter: 'active'
}
# Convert parameters to query string
query_string = URI.encode_www_form(params)
uri = URI("#{base_uri}?#{query_string}")
puts "Request URL: #{uri}"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'MyApp/1.0 (Ruby Net::HTTP)'
request['Accept'] = 'application/json'
request['Authorization'] = 'Bearer your-access-token'
response = http.request(request)
case response
when Net::HTTPSuccess
puts "Success: #{response.code}"
JSON.parse(response.body)
when Net::HTTPRedirection
puts "Redirected: #{response['location']}"
# Handle redirection
nil
when Net::HTTPClientError
puts "Client Error: #{response.code} - #{response.message}"
nil
when Net::HTTPServerError
puts "Server Error: #{response.code} - #{response.message}"
nil
else
puts "Unknown response: #{response.code}"
nil
end
end
# POST request (sending JSON)
def post_json_request
uri = URI('https://api.example.com/users')
user_data = {
name: 'John Doe',
email: '[email protected]',
age: 30,
department: 'Development'
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.read_timeout = 30
http.open_timeout = 10
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request['Accept'] = 'application/json'
request['Authorization'] = 'Bearer your-access-token'
request['User-Agent'] = 'MyApp/1.0 (Ruby)'
request.body = JSON.generate(user_data)
begin
response = http.request(request)
puts "POST Response: #{response.code}"
puts "Response Headers: #{response.to_hash}"
if response.code == '201'
created_user = JSON.parse(response.body)
puts "User created successfully: #{created_user}"
return created_user
else
puts "Error creating user: #{response.body}"
return nil
end
rescue Net::TimeoutError => e
puts "Request timeout: #{e.message}"
return nil
rescue StandardError => e
puts "Request error: #{e.message}"
return nil
end
end
# POST request (sending form data)
def post_form_data
uri = URI('https://api.example.com/login')
form_data = {
'username' => 'testuser',
'password' => 'secret123',
'remember_me' => 'true'
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/x-www-form-urlencoded'
request['User-Agent'] = 'MyApp/1.0 (Ruby)'
request.set_form_data(form_data)
response = http.request(request)
puts "Login Response: #{response.code}"
puts "Set-Cookie: #{response['Set-Cookie']}" if response['Set-Cookie']
response
end
# PUT request (data update)
def put_request
uri = URI('https://api.example.com/users/123')
update_data = {
name: 'Jane Doe',
email: '[email protected]',
age: 31
}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Put.new(uri)
request['Content-Type'] = 'application/json'
request['Authorization'] = 'Bearer your-access-token'
request.body = JSON.generate(update_data)
response = http.request(request)
if response.is_a?(Net::HTTPSuccess)
puts "User updated successfully"
JSON.parse(response.body)
else
puts "Update failed: #{response.code} - #{response.body}"
nil
end
end
# DELETE request
def delete_request
uri = URI('https://api.example.com/users/123')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Delete.new(uri)
request['Authorization'] = 'Bearer your-access-token'
response = http.request(request)
case response.code
when '204'
puts "User deleted successfully"
true
when '404'
puts "User not found"
false
else
puts "Delete failed: #{response.code} - #{response.body}"
false
end
end
# Detailed response analysis
def analyze_response(response)
puts "=== Response Analysis ==="
puts "Status: #{response.code} #{response.message}"
puts "HTTP Version: #{response.http_version}"
puts "Content-Type: #{response.content_type}"
puts "Content-Length: #{response.content_length}"
puts "Last-Modified: #{response['Last-Modified']}"
puts "ETag: #{response['ETag']}"
puts "Cache-Control: #{response['Cache-Control']}"
# Response body character encoding
if response.body
puts "Body Encoding: #{response.body.encoding}"
puts "Body Size: #{response.body.bytesize} bytes"
end
puts "========================"
end
# Usage examples
simple_get_request
get_request_with_params
post_json_request
post_form_data
put_request
delete_request
Advanced Configuration and Customization (Authentication, SSL, Sessions, etc.)
require 'net/http'
require 'openssl'
require 'base64'
# Basic authentication
def basic_auth_request
uri = URI('https://api.example.com/private')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request.basic_auth('username', 'password')
# Or manually set header
# credentials = Base64.strict_encode64("username:password")
# request['Authorization'] = "Basic #{credentials}"
response = http.request(request)
puts "Basic Auth Response: #{response.code}"
response
end
# Custom headers and timeout configuration
def custom_headers_and_timeout
uri = URI('https://api.example.com/data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# Timeout settings
http.open_timeout = 10 # Connection timeout (seconds)
http.read_timeout = 30 # Read timeout (seconds)
http.write_timeout = 30 # Write timeout (seconds)
http.continue_timeout = 1 # 100-continue timeout (seconds)
request = Net::HTTP::Get.new(uri)
# Custom header settings
request['User-Agent'] = 'MyApp/1.0 (Ruby Net::HTTP)'
request['Accept'] = 'application/json, application/xml'
request['Accept-Language'] = 'en-US, ja-JP'
request['Accept-Encoding'] = 'gzip, deflate, br'
request['Cache-Control'] = 'no-cache'
request['X-API-Version'] = 'v2'
request['X-Request-ID'] = SecureRandom.uuid
request['X-Client-Info'] = 'Ruby/3.2.0 Net::HTTP/0.4.0'
begin
response = http.request(request)
puts "Custom Request Response: #{response.code}"
# Check response compression
if response['Content-Encoding']
puts "Content-Encoding: #{response['Content-Encoding']}"
end
response
rescue Net::OpenTimeout => e
puts "Connection timeout: #{e.message}"
nil
rescue Net::ReadTimeout => e
puts "Read timeout: #{e.message}"
nil
end
end
# SSL/TLS configuration and client certificates
def ssl_configuration
uri = URI('https://secure-api.example.com/data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# SSL settings
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_depth = 5
# CA certificate specification
http.ca_file = '/path/to/ca-bundle.crt' # CA certificate file
# http.ca_path = '/path/to/ca-certs/' # CA certificate directory
# Client certificate configuration
http.cert = OpenSSL::X509::Certificate.new(File.read('/path/to/client.crt'))
http.key = OpenSSL::PKey::RSA.new(File.read('/path/to/client.key'), 'key-password')
# Encryption settings
http.ssl_version = :TLSv1_2 # TLS version specification
http.ciphers = 'HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA'
# Detailed SSL certificate verification (disable verification in development environment)
# http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Not recommended for production
# SSL certificate verification callback
http.verify_callback = proc do |preverify_ok, ssl_context|
if preverify_ok
puts "SSL Certificate verification: OK"
else
puts "SSL Certificate verification failed: #{ssl_context.error_string}"
end
preverify_ok
end
request = Net::HTTP::Get.new(uri)
begin
response = http.request(request)
puts "SSL Request Response: #{response.code}"
# Display SSL connection information
puts "SSL Cipher: #{http.peer_cert.to_text}" if http.peer_cert
response
rescue OpenSSL::SSL::SSLError => e
puts "SSL Error: #{e.message}"
nil
end
end
# Proxy server configuration
def proxy_configuration
# Proxy information
proxy_host = 'proxy.example.com'
proxy_port = 8080
proxy_user = 'proxy_username'
proxy_pass = 'proxy_password'
uri = URI('https://api.example.com/data')
# HTTP connection with proxy
http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port, proxy_user, proxy_pass)
http.use_ssl = true
# When proxy authentication is not required
# http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
request = Net::HTTP::Get.new(uri)
begin
response = http.request(request)
puts "Proxy Request Response: #{response.code}"
response
rescue Net::ProxyError => e
puts "Proxy Error: #{e.message}"
nil
end
end
# Session management and cookie processing
class HTTPSession
def initialize(base_url)
@uri = URI(base_url)
@cookies = {}
@http = create_http_connection
end
def create_http_connection
http = Net::HTTP.new(@uri.host, @uri.port)
http.use_ssl = @uri.scheme == 'https'
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.read_timeout = 30
http.open_timeout = 10
http
end
def get(path, headers = {})
request = Net::HTTP::Get.new(path)
add_headers(request, headers)
add_cookies(request)
response = @http.request(request)
extract_cookies(response)
response
end
def post(path, data = nil, headers = {})
request = Net::HTTP::Post.new(path)
add_headers(request, headers)
add_cookies(request)
if data
if headers['Content-Type'] == 'application/json'
request.body = JSON.generate(data)
else
request.set_form_data(data)
end
end
response = @http.request(request)
extract_cookies(response)
response
end
def login(username, password, login_path = '/login')
login_data = {
username: username,
password: password
}
response = post(login_path, login_data)
if response.is_a?(Net::HTTPSuccess)
puts "Login successful"
true
else
puts "Login failed: #{response.code}"
false
end
end
def close
@http.finish if @http.started?
end
private
def add_headers(request, headers)
default_headers = {
'User-Agent' => 'Ruby HTTPSession/1.0',
'Accept' => 'application/json'
}
default_headers.merge(headers).each do |key, value|
request[key] = value
end
end
def add_cookies(request)
return if @cookies.empty?
cookie_string = @cookies.map { |name, value| "#{name}=#{value}" }.join('; ')
request['Cookie'] = cookie_string
end
def extract_cookies(response)
return unless response['Set-Cookie']
response.get_fields('Set-Cookie').each do |cookie|
# Cookie parsing (simplified version)
cookie_parts = cookie.split(';').first.split('=', 2)
next unless cookie_parts.size == 2
name, value = cookie_parts
@cookies[name.strip] = value.strip
end
end
end
# Session usage example
def session_example
session = HTTPSession.new('https://api.example.com')
begin
# Login
if session.login('testuser', 'password123')
# Call API that requires authentication
response = session.get('/profile')
puts "Profile Response: #{response.code}"
# Post data
post_data = { message: 'Hello from Ruby!' }
response = session.post('/posts', post_data, { 'Content-Type' => 'application/json' })
puts "Post Response: #{response.code}"
end
ensure
session.close
end
end
# Usage examples
basic_auth_request
custom_headers_and_timeout
ssl_configuration
proxy_configuration
session_example
Error Handling and Retry Functionality
require 'net/http'
require 'uri'
require 'json'
# Comprehensive error handling
class HTTPClient
class << self
def safe_request(method, url, options = {})
uri = URI(url)
retries = options[:retries] || 3
timeout = options[:timeout] || 30
headers = options[:headers] || {}
body = options[:body]
(retries + 1).times do |attempt|
begin
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = timeout
http.open_timeout = timeout / 3
request = create_request(method, uri, headers, body)
response = http.request(request)
# Success response determination
if response.is_a?(Net::HTTPSuccess)
return parse_response(response, options[:parse_json])
elsif response.is_a?(Net::HTTPServerError) && attempt < retries
puts "Server error (#{response.code}), retrying in #{attempt + 1} seconds..."
sleep(attempt + 1)
next
else
handle_http_error(response)
return nil
end
rescue Net::TimeoutError => e
if attempt < retries
puts "Timeout error, retrying in #{attempt + 1} seconds..."
sleep(attempt + 1)
next
else
puts "Request timeout after #{retries + 1} attempts: #{e.message}"
return nil
end
rescue Net::ConnectionRefused => e
if attempt < retries
puts "Connection refused, retrying in #{(attempt + 1) * 2} seconds..."
sleep((attempt + 1) * 2)
next
else
puts "Connection refused after #{retries + 1} attempts: #{e.message}"
return nil
end
rescue SocketError => e
puts "DNS/Socket error: #{e.message}"
return nil
rescue OpenSSL::SSL::SSLError => e
puts "SSL error: #{e.message}"
return nil
rescue StandardError => e
puts "Unexpected error: #{e.class} - #{e.message}"
puts e.backtrace.first(5) if options[:debug]
return nil
end
end
nil
end
private
def create_request(method, uri, headers, body)
case method.to_s.upcase
when 'GET'
request = Net::HTTP::Get.new(uri)
when 'POST'
request = Net::HTTP::Post.new(uri)
when 'PUT'
request = Net::HTTP::Put.new(uri)
when 'DELETE'
request = Net::HTTP::Delete.new(uri)
when 'PATCH'
request = Net::HTTP::Patch.new(uri)
else
raise ArgumentError, "Unsupported HTTP method: #{method}"
end
# Header settings
headers.each { |key, value| request[key] = value }
# Body settings
if body
if headers['Content-Type'] == 'application/json'
request.body = body.is_a?(String) ? body : JSON.generate(body)
else
request.body = body
end
end
request
end
def parse_response(response, parse_json = true)
content_type = response.content_type
if parse_json && content_type&.include?('application/json')
begin
JSON.parse(response.body)
rescue JSON::ParserError => e
puts "JSON parse error: #{e.message}"
response.body
end
else
response.body
end
end
def handle_http_error(response)
case response.code.to_i
when 400
puts "Bad Request (400): Invalid request"
when 401
puts "Unauthorized (401): Authentication required"
when 403
puts "Forbidden (403): Access denied"
when 404
puts "Not Found (404): Resource not found"
when 409
puts "Conflict (409): Resource conflict occurred"
when 422
puts "Unprocessable Entity (422): Validation error"
puts response.body if response.body
when 429
puts "Too Many Requests (429): Rate limit reached"
puts "Retry-After: #{response['Retry-After']}" if response['Retry-After']
when 500
puts "Internal Server Error (500): Server internal error"
when 502
puts "Bad Gateway (502): Proxy server error"
when 503
puts "Service Unavailable (503): Service unavailable"
when 504
puts "Gateway Timeout (504): Gateway timeout"
else
puts "HTTP Error #{response.code}: #{response.message}"
puts response.body if response.body
end
end
end
end
# Detailed response analysis class
class ResponseAnalyzer
def self.analyze(response)
puts "=== Response Analysis ==="
puts "Status: #{response.code} #{response.message}"
puts "HTTP Version: #{response.http_version}"
# Header analysis
analyze_headers(response)
# Body analysis
analyze_body(response)
# Performance analysis
analyze_performance(response)
puts "========================"
end
private
def self.analyze_headers(response)
puts "\n--- Headers Analysis ---"
puts "Content-Type: #{response.content_type}"
puts "Content-Length: #{response.content_length}"
puts "Content-Encoding: #{response['Content-Encoding']}" if response['Content-Encoding']
puts "Cache-Control: #{response['Cache-Control']}" if response['Cache-Control']
puts "ETag: #{response['ETag']}" if response['ETag']
puts "Last-Modified: #{response['Last-Modified']}" if response['Last-Modified']
puts "Server: #{response['Server']}" if response['Server']
puts "Set-Cookie: #{response['Set-Cookie']}" if response['Set-Cookie']
end
def self.analyze_body(response)
puts "\n--- Body Analysis ---"
if response.body
puts "Body Size: #{response.body.bytesize} bytes"
puts "Body Encoding: #{response.body.encoding}"
# Check if JSON
if response.content_type&.include?('application/json')
begin
json_data = JSON.parse(response.body)
puts "JSON Structure: #{analyze_json_structure(json_data)}"
rescue JSON::ParserError
puts "Invalid JSON format"
end
end
else
puts "No body content"
end
end
def self.analyze_performance(response)
puts "\n--- Performance Info ---"
# Note: Detailed timing information is not available by default in Net::HTTP
puts "Response Headers Count: #{response.to_hash.keys.length}"
puts "Keep-Alive: #{response['Connection']}" if response['Connection']
end
def self.analyze_json_structure(data, level = 0)
indent = " " * level
case data
when Hash
"Hash(#{data.keys.length} keys: #{data.keys.first(3).join(', ')}#{data.keys.length > 3 ? '...' : ''})"
when Array
"Array(#{data.length} items)"
else
data.class.name
end
end
end
# Practical API client example
class RestAPIClient
def initialize(base_url, api_key = nil)
@base_url = base_url.chomp('/')
@api_key = api_key
end
def get(endpoint, params = {})
url = build_url(endpoint, params)
HTTPClient.safe_request(:get, url, {
headers: default_headers,
parse_json: true,
retries: 3,
timeout: 30
})
end
def post(endpoint, data = {})
url = build_url(endpoint)
HTTPClient.safe_request(:post, url, {
headers: default_headers.merge('Content-Type' => 'application/json'),
body: data,
parse_json: true,
retries: 2,
timeout: 45
})
end
def put(endpoint, data = {})
url = build_url(endpoint)
HTTPClient.safe_request(:put, url, {
headers: default_headers.merge('Content-Type' => 'application/json'),
body: data,
parse_json: true,
retries: 2,
timeout: 45
})
end
def delete(endpoint)
url = build_url(endpoint)
HTTPClient.safe_request(:delete, url, {
headers: default_headers,
parse_json: false,
retries: 2,
timeout: 30
})
end
private
def build_url(endpoint, params = {})
url = "#{@base_url}/#{endpoint.gsub(/^\//, '')}"
unless params.empty?
query_string = URI.encode_www_form(params)
url += "?#{query_string}"
end
url
end
def default_headers
headers = {
'User-Agent' => 'RestAPIClient/1.0 (Ruby Net::HTTP)',
'Accept' => 'application/json'
}
headers['Authorization'] = "Bearer #{@api_key}" if @api_key
headers
end
end
# Usage examples
def error_handling_examples
# Basic usage example
result = HTTPClient.safe_request(:get, 'https://api.example.com/users', {
headers: { 'Authorization' => 'Bearer token123' },
parse_json: true,
retries: 3,
timeout: 30,
debug: true
})
puts "Result: #{result}"
# API client usage example
client = RestAPIClient.new('https://api.example.com', 'your-api-key')
users = client.get('users', { page: 1, limit: 10 })
puts "Users: #{users}"
new_user = client.post('users', { name: 'John Doe', email: '[email protected]' })
puts "Created user: #{new_user}"
end
error_handling_examples
File Upload, Download, and Form Processing
require 'net/http'
require 'net/http/post/multipart' # gem install multipart-post
require 'uri'
require 'mime/types'
# File upload (multipart format)
def upload_file(file_path, upload_url, additional_fields = {})
uri = URI(upload_url)
# Check file existence
unless File.exist?(file_path)
puts "File not found: #{file_path}"
return nil
end
# MIME type detection
mime_type = MIME::Types.type_for(file_path).first&.content_type || 'application/octet-stream'
# Prepare multipart data
File.open(file_path, 'rb') do |file|
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
http.read_timeout = 300 # 5-minute timeout
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer your-upload-token'
# Build form data
form_data = additional_fields.merge(
'file' => UploadIO.new(file, mime_type, File.basename(file_path))
)
request.set_form(form_data, 'multipart/form-data')
begin
puts "Uploading file: #{file_path} (#{File.size(file_path)} bytes)"
response = http.request(request)
case response.code
when '200', '201'
puts "Upload successful: #{response.code}"
JSON.parse(response.body) if response.body
else
puts "Upload failed: #{response.code} - #{response.body}"
nil
end
rescue StandardError => e
puts "Upload error: #{e.message}"
nil
end
end
end
# Manual multipart form (implementation without external gems)
def upload_file_manual(file_path, upload_url, fields = {})
uri = URI(upload_url)
boundary = "----WebKitFormBoundary#{SecureRandom.hex(16)}"
File.open(file_path, 'rb') do |file|
# Build multipart body
body = []
# Additional fields
fields.each do |name, value|
body << "--#{boundary}\r\n"
body << "Content-Disposition: form-data; name=\"#{name}\"\r\n"
body << "\r\n"
body << "#{value}\r\n"
end
# File part
body << "--#{boundary}\r\n"
body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{File.basename(file_path)}\"\r\n"
body << "Content-Type: #{MIME::Types.type_for(file_path).first&.content_type || 'application/octet-stream'}\r\n"
body << "\r\n"
body << file.read
body << "\r\n"
body << "--#{boundary}--\r\n"
request_body = body.join
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
request['Content-Length'] = request_body.bytesize.to_s
request['Authorization'] = 'Bearer your-token'
request.body = request_body
response = http.request(request)
puts "Manual upload response: #{response.code}"
response
end
end
# File download
def download_file(download_url, output_path, chunk_size = 8192)
uri = URI(download_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri)
request['User-Agent'] = 'Ruby FileDownloader/1.0'
begin
# Create directory
FileUtils.mkdir_p(File.dirname(output_path))
http.request(request) do |response|
case response.code
when '200'
total_size = response['Content-Length']&.to_i
downloaded = 0
puts "Downloading: #{download_url}"
puts "Total size: #{total_size ? "#{total_size} bytes" : "unknown"}"
File.open(output_path, 'wb') do |file|
response.read_body do |chunk|
file.write(chunk)
downloaded += chunk.bytesize
# Progress display
if total_size
progress = (downloaded.to_f / total_size * 100).round(1)
print "\rProgress: #{progress}% (#{downloaded}/#{total_size} bytes)"
else
print "\rDownloaded: #{downloaded} bytes"
end
end
end
puts "\nDownload completed: #{output_path}"
return true
when '404'
puts "File not found: #{download_url}"
return false
else
puts "Download failed: #{response.code} - #{response.message}"
return false
end
end
rescue StandardError => e
puts "Download error: #{e.message}"
return false
end
end
# Large file streaming download
def stream_download(url, output_path)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
http.request(request) do |response|
if response.code == '200'
File.open(output_path, 'wb') do |file|
response.read_body { |chunk| file.write(chunk) }
end
puts "Stream download completed: #{output_path}"
else
puts "Stream download failed: #{response.code}"
end
end
end
end
# Form data processing
class FormProcessor
def self.submit_form(form_url, form_data, file_fields = {})
uri = URI(form_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
if file_fields.any?
# Form with files
submit_multipart_form(request, form_data, file_fields)
else
# Regular form
request.set_form_data(form_data)
end
response = http.request(request)
puts "Form submission response: #{response.code}"
response
end
def self.submit_json_form(form_url, json_data)
uri = URI(form_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = JSON.generate(json_data)
response = http.request(request)
puts "JSON form submission response: #{response.code}"
response
end
private
def self.submit_multipart_form(request, form_data, file_fields)
# multipart/form-data implementation
boundary = "----FormBoundary#{SecureRandom.hex(16)}"
body = []
# Text fields
form_data.each do |name, value|
body << "--#{boundary}\r\n"
body << "Content-Disposition: form-data; name=\"#{name}\"\r\n"
body << "\r\n"
body << "#{value}\r\n"
end
# File fields
file_fields.each do |name, file_path|
next unless File.exist?(file_path)
File.open(file_path, 'rb') do |file|
body << "--#{boundary}\r\n"
body << "Content-Disposition: form-data; name=\"#{name}\"; filename=\"#{File.basename(file_path)}\"\r\n"
body << "Content-Type: #{MIME::Types.type_for(file_path).first&.content_type || 'application/octet-stream'}\r\n"
body << "\r\n"
body << file.read
body << "\r\n"
end
end
body << "--#{boundary}--\r\n"
request['Content-Type'] = "multipart/form-data; boundary=#{boundary}"
request.body = body.join
end
end
# Usage examples and tests
def file_operations_examples
# File upload example
upload_result = upload_file(
'/path/to/document.pdf',
'https://api.example.com/upload',
{
'category' => 'documents',
'public' => 'false',
'description' => 'Test document'
}
)
puts "Upload result: #{upload_result}"
# File download example
download_success = download_file(
'https://api.example.com/files/sample.zip',
'/tmp/downloads/sample.zip'
)
puts "Download success: #{download_success}"
# Form submission example
form_response = FormProcessor.submit_form(
'https://example.com/contact',
{
'name' => 'John Doe',
'email' => '[email protected]',
'message' => 'Contact inquiry'
},
{
'attachment' => '/path/to/attachment.pdf'
}
)
puts "Form response: #{form_response.code}"
# JSON form submission example
json_response = FormProcessor.submit_json_form(
'https://api.example.com/feedback',
{
user_id: 123,
rating: 5,
comment: 'Excellent service',
timestamp: Time.now.iso8601
}
)
puts "JSON form response: #{json_response.code}"
end
file_operations_examples