LightTCP::SSLclient

SSL/TLS HTTP client library for Perl with proxy support, certificate pinning, and redirect following.

Tests

Features

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:

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

License

This module is free software and may be modified and distributed under the same terms as Perl itself.