LightTCP::SSLclient
SSL/TLS HTTP client library for Perl with proxy support, certificate pinning, and redirect following.
Features
- SSL/TLS Connections - Secure HTTP client with configurable protocols and cipher suites
- HTTP CONNECT Proxy Support - Full support for HTTP proxy with Basic authentication
- Certificate Pinning - Save and verify certificate fingerprints
- Redirect Following - Automatic redirect handling with configurable limits
- Keep-Alive Connections - Reuse connections for better performance
- Chunked Transfer Encoding - Proper handling of chunked responses
- Verbose Debug Mode - Detailed debugging output
Installation
Prerequisites
# Install dependencies
cpanm --installdeps .
Standard Installation
# Build and install
perl Makefile.PL
make
make install
Direct Usage
# Run without installing
perl -I lib examples/ssltest.pl https://example.com
Quick Start
use LightTCP::SSLclient;
# Create client with default options
my $client = LightTCP::SSLclient->new(
timeout => 30,
insecure => 0,
verbose => 0,
);
# Connect to host
my ($ok, $errors, $debug, $code) = $client->connect('example.com', 443);
die "Connect failed: @$errors" unless $ok;
# Send HTTP request
($ok, $errors, $debug, $code) = $client->request('GET', '/api/data', host => 'example.com');
die "Request failed: @$errors" unless $ok;
# Read response
my ($code, $state, $headers, $body, $resp_errors, $resp_debug, $resp_code) = $client->response();
print "Response: $code $state\n";
print $body;
$client->close();
Constructor Options
my $client = LightTCP::SSLclient->new(
timeout => 10, # Request timeout in seconds
insecure => 0, # Skip SSL verification (0=verify, 1=skip)
cert => '/path/to/client', # Client certificate base path
verbose => 0, # Enable debug output
user_agent => 'MyClient/1.0', # User-Agent header
ssl_protocols => ['TLSv1.2', 'TLSv1.3'], # Allowed SSL protocols
ssl_ciphers => 'HIGH:!aNULL:!MD5', # Allowed cipher suites
keep_alive => 0, # Use HTTP keep-alive
buffer_size => 8192, # Read buffer size in bytes
max_redirects => 5, # Max redirects to follow
follow_redirects => 1, # Follow 3xx redirects
);
API Reference
Connection Methods
connect($host, $port, $proxy, $proxy_auth)
Establish SSL connection to target host.
my ($ok, $errors, $debug, $code) = $client->connect(
'example.com', # Target host
443, # Target port
'proxy.com:8080', # Optional HTTP proxy
'user:pass', # Optional proxy auth
);
Returns: (ok, errors_ref, debug_ref, error_code)
reconnect()
Reconnect using previously used connection parameters.
my ($ok, $errors, $debug, $code) = $client->reconnect();
close()
Close the connection.
$client->close();
Request Methods
request($method, $path, %options)
Send HTTP request.
my ($ok, $errors, $debug, $code) = $client->request(
'GET', # Method
'/api/data', # Path
host => 'example.com', # Host header
payload => $body, # Request body (optional)
headers => { # Custom headers
'X-Custom' => 'value',
},
);
response()
Read HTTP response.
my ($code, $state, $headers, $body, $errors, $debug, $code) = $client->response();
request_with_redirects($method, $path, %options)
Send HTTP request and automatically follow redirects.
my ($code, $state, $headers, $body, $errors, $debug, $resp_code, $history)
= $client->request_with_redirects(
'POST', # Method
'/submit', # Path
host => 'example.com', # Host header
payload => $form_data, # Request body
);
# Check redirect history
foreach my $redirect (@$history) {
print "$redirect->{code}: $redirect->{from} -> $redirect->{to}\n";
}
Redirect Behavior:
- 301/302: POST requests converted to GET, payload dropped
- 303: Always converted to GET
- 307/308: Method preserved (POST stays POST)
Fingerprint Methods
fingerprint_read($dir, $host, $port)
Read saved certificate fingerprint.
my $fp = $client->fingerprint_read('./certs', 'example.com', 443);
fingerprint_save($dir, $host, $port, $fp, $save)
Save certificate fingerprint.
my ($ok, $errors, $debug, $code) = $client->fingerprint_save(
$dir, $host, $port, $fingerprint, $save
);
# $save = 1 to permanently save, 0 to save as .new file
Accessor Methods
| Method | Description |
|--------|-------------|
| socket() | Returns underlying socket object |
| is_connected() | Returns 1 if connected, 0 otherwise |
| get_timeout(), set_timeout($value) | Get/set timeout |
| get_user_agent(), set_user_agent($value) | Get/set User-Agent |
| get_insecure(), set_insecure($value) | Get/set insecure mode |
| get_keep_alive(), set_keep_alive($value) | Get/set keep-alive |
| is_keep_alive() | Boolean keep-alive check |
| get_cert(), set_cert($value) | Get/set client certificate |
| get_ssl_protocols() | Get allowed SSL protocols |
| get_ssl_ciphers() | Get allowed cipher suites |
| get_buffer_size(), set_buffer_size($value) | Get/set buffer size |
| get_max_redirects(), set_max_redirects($value) | Get/set max redirects |
| get_follow_redirects(), set_follow_redirects($value) | Get/set redirect following |
| get_redirect_count() | Get redirects followed in last request |
| get_redirect_history() | Get redirect history arrayref |
| is_verbose() | Get verbose mode status |
Return Values
connect() and request()
my ($ok, $errors_ref, $debug_ref, $error_code) = $client->method(...);
| Value | Description |
|-------|-------------|
| $ok | Boolean success indicator (1=success, 0=failure) |
| $errors_ref | Reference to array of error messages |
| $debug_ref | Reference to array of debug messages (empty unless verbose) |
| $error_code | Error type constant (0 if successful) |
Error Codes
use LightTCP::SSLclient qw(ECONNECT EREQUEST ERESPONSE ETIMEOUT ESSL);
my ($ok, $errors, $debug, $code) = $client->connect(...);
if (!$ok) {
if ($code == ECONNECT) { die "Connection error" }
elsif ($code == EREQUEST) { die "Request error" }
elsif ($code == ERESPONSE) { die "Response error" }
elsif ($code == ETIMEOUT) { die "Timeout error" }
elsif ($code == ESSL) { die "SSL/TLS error" }
}
| Constant | Value | Description |
|----------|-------|-------------|
| ECONNECT | 1 | Connection error |
| EREQUEST | 2 | Request error |
| ERESPONSE | 3 | Response error |
| ETIMEOUT | 4 | Timeout error |
| ESSL | 5 | SSL/TLS error |
response()
my ($code, $state, $headers_ref, $body, $errors_ref, $debug_ref, $error_code)
= $client->response();
| Value | Description |
|-------|-------------|
| $code | HTTP status code (200, 404, etc.) |
| $state | HTTP status message (OK, Not Found) |
| $headers_ref | Reference to hash of response headers (lowercase keys) |
| $body | Response body as string |
| $errors_ref, $debug_ref, $error_code | Same as above |
Examples
Basic GET Request
use LightTCP::SSLclient;
my $client = LightTCP::SSLclient->new(timeout => 30);
my ($ok, $errors, $debug) = $client->connect('example.com', 443);
die "Connect failed: @$errors" unless $ok;
($ok, $errors, $debug) = $client->request('GET', '/');
die "Request failed: @$errors" unless $ok;
my ($code, $state, $headers, $body) = $client->response();
print "Response: $code $state\n";
print $body;
$client->close();
With Proxy Authentication
my ($ok, $errors, $debug) = $client->connect(
'api.example.com', 443,
'proxy.corp.com:8080', 'user:pass'
);
With Certificate Pinning
my $client = LightTCP::SSLclient->new(dir => './certs');
my ($ok, $errors, $debug) = $client->connect('example.com', 443);
# First connection - save fingerprint
$client->fingerprint_save('./certs', 'example.com', 443, $fp, 1);
# Subsequent connections will auto-verify
$client->fingerprint_save('./certs', 'example.com', 443, $fp, 0);
Verbose Debug Mode
my $client = LightTCP::SSLclient->new(verbose => 1);
my ($ok, $errors, $debug) = $client->connect('example.com', 443);
print "=== DEBUG ===\n";
print @$debug;
print "=== ERRORS ===\n";
print @$errors;
Following Redirects
my ($code, $state, $headers, $body, $errors, $debug, $resp_code, $history)
= $client->request_with_redirects('GET', '/old-page', host => 'example.com');
print "Final URL: $headers->{'content-location'} // $body\n" if $code == 200;
# Show redirect chain
for my $r (@$history) {
print "$r->{code}: $r->{from} -> $r->{to}\n";
}
Testing
# Run all tests
make test
# Run single test file
prove t/00_load.t
# Run with verbose output
prove -v t/01_constructor.t
# Run without building first
perl -I lib t/00_load.t
Requirements
- Perl 5.8.1+
IO::Socket::SSLIO::Socket::INETMIME::Base64URI
License
This module is free software and may be modified and distributed under the same terms as Perl itself.