package Net::HTTPS::Any; use warnings; use strict; use base qw( Exporter ); use vars qw(@EXPORT_OK $ssl_module $skip_NetSSLeay); use URI::Escape; use Tie::IxHash; @EXPORT_OK = qw( https_get https_post ); BEGIN { $ssl_module = ''; eval { die if defined($skip_NetSSLeay) && $skip_NetSSLeay; require Net::SSLeay; Net::SSLeay->VERSION(1.30); #import Net::SSLeay # qw(get_https post_https make_form make_headers); $ssl_module = 'Net::SSLeay'; }; if ($@) { eval { require LWP::UserAgent; require HTTP::Request::Common; require Crypt::SSLeay; #import HTTP::Request::Common qw(GET POST); $ssl_module = 'Crypt::SSLeay'; }; } unless ($ssl_module) { die "One of Net::SSLeay (v1.30 or later)" . " or Crypt::SSLeay (+LWP) is required"; } } =head1 NAME Net::HTTPS::Any - Simple HTTPS client using whichever underlying SSL module is available =cut our $VERSION = '0.11'; =head1 SYNOPSIS use Net::HTTPS::Any qw(https_get https_post); ( $page, $response, %reply_headers ) = https_get( { 'host' => 'www.fortify.net', 'port' => 443, 'path' => '/sslcheck.html', 'args' => { 'field' => 'value' }, #'args' => [ 'field'=>'value' ], #order preserved }, ); ( $page, $response, %reply_headers ) = https_post( 'host' => 'www.google.com', 'port' => 443, 'path' => '/accounts/ServiceLoginAuth', 'args' => { 'field' => 'value' }, #'args' => [ 'field'=>'value' ], #order preserved ); #... =head1 DESCRIPTION This is a simple wrapper around either of the two available SSL modules. It offers a unified API for sending GET and POST requests over HTTPS and receiving responses. It depends on Net::SSLeay _or_ ( Crypt::SSLeay and LWP::UserAgent ). =head1 WHY THIS MODULE If you just want to write something that speaks HTTPS, you don't need this module. Just go ahead and use whichever of the two modules is good for you. Don't worry about it. On the other hand, if you are a CPAN author or distribute a Perl application, especially if you aim to support multiple OSes/disributions, using this module for speaking HTTPS may make things easier on your users. It allows your code to be used with either SSL implementation. =head1 FUNCTIONS =head2 https_get HASHREF | FIELD => VALUE, ... Accepts parameters as either a hashref or a list of fields and values. Parameters are: =over 4 =item host =item port =item path =item headers (hashref) For example: { 'X-Header1' => 'value', ... } =cut # =item Content-Type # # Defaults to "application/x-www-form-urlencoded" if not specified. =item args CGI arguments, eitehr as a hashref or a listref. In the latter case, ordering is preserved (see L to do so when passing a hashref). =item debug Set true to enable debugging. =back Returns a list consisting of the page content as a string, the HTTP response code and message (i.e. "200 OK" or "404 Not Found"), and a list of key/value pairs representing the HTTP response headers. =cut sub https_get { my $opts = ref($_[0]) ? shift : { @_ }; #hashref or list # accept a hashref or a list (keep it ordered) my $post_data = {}; # technically get_data, pedant if ( exists($opts->{'args'}) && ref($opts->{'args'}) eq 'HASH' ) { $post_data = $opts->{'args'}; } elsif ( exists($opts->{'args'}) && ref($opts->{'args'}) eq 'ARRAY' ) { tie my %hash, 'Tie::IxHash', @{ $opts->{'args'} }; $post_data = \%hash; } $opts->{'port'} ||= 443; #$opts->{"Content-Type"} ||= "application/x-www-form-urlencoded"; ### XXX referer!!! my %headers = (); if ( ref( $opts->{headers} ) eq "HASH" ) { %headers = %{ $opts->{headers} }; } $headers{'Host'} ||= $opts->{'host'}; my $path = $opts->{'path'}; if ( keys %$post_data ) { $path .= '?' . join( ';', map { uri_escape($_) . '=' . uri_escape( $post_data->{$_} ) } keys %$post_data ); } if ( $ssl_module eq 'Net::SSLeay' ) { no warnings 'uninitialized'; import Net::SSLeay qw(get_https make_headers); my $headers = make_headers(%headers); $Net::SSLeay::trace = $opts->{'debug'} if exists $opts->{'debug'} && $opts->{'debug'}; my( $res_page, $res_code, @res_headers ) = get_https( $opts->{'host'}, $opts->{'port'}, $path, $headers, #"", #$opts->{"Content-Type"}, ); $res_code =~ /^(HTTP\S+ )?(.*)/ and $res_code = $2; return ( $res_page, $res_code, @res_headers ); } elsif ( $ssl_module eq 'Crypt::SSLeay' ) { import HTTP::Request::Common qw(GET); my $url = 'https://' . $opts->{'host'}; $url .= ':' . $opts->{'port'} unless $opts->{'port'} == 443; $url .= "/$path"; my $ua = new LWP::UserAgent; foreach my $hdr ( keys %headers ) { $ua->default_header( $hdr => $headers{$hdr} ); } $ENV{HTTPS_DEBUG} = $opts->{'debug'} if exists $opts->{'debug'}; my $res = $ua->request( GET($url) ); my @res_headers = map { $_ => $res->header($_) } $res->header_field_names; return ( $res->content, $res->code. ' '. $res->message, @res_headers ); } else { die "unknown SSL module $ssl_module"; } } =head2 https_post HASHREF | FIELD => VALUE, ... Accepts parameters as either a hashref or a list of fields and values. Parameters are: =over 4 =item host =item port =item path =item headers (hashref) For example: { 'X-Header1' => 'value', ... } =item Content-Type Defaults to "application/x-www-form-urlencoded" if not specified. =item args CGI arguments, eitehr as a hashref or a listref. In the latter case, ordering is preserved (see L to do so when passing a hashref). =item content Raw content (overrides args). A simple scalar containing the raw content. =item debug Set true to enable debugging in the underlying SSL module. =back Returns a list consisting of the page content as a string, the HTTP response code and message (i.e. "200 OK" or "404 Not Found"), and a list of key/value pairs representing the HTTP response headers. =cut sub https_post { my $opts = ref($_[0]) ? shift : { @_ }; #hashref or list # accept a hashref or a list (keep it ordered). or a scalar of content. my $post_data = ''; if ( exists($opts->{'args'}) && ref($opts->{'args'}) eq 'HASH' ) { $post_data = $opts->{'args'}; } elsif ( exists($opts->{'args'}) && ref($opts->{'args'}) eq 'ARRAY' ) { tie my %hash, 'Tie::IxHash', @{ $opts->{'args'} }; $post_data = \%hash; } if ( exists $opts->{'content'} ) { $post_data = $opts->{'content'}; } $opts->{'port'} ||= 443; $opts->{"Content-Type"} ||= "application/x-www-form-urlencoded"; ### XXX referer!!! my %headers; if ( ref( $opts->{headers} ) eq "HASH" ) { %headers = %{ $opts->{headers} }; } $headers{'Host'} ||= $opts->{'host'}; if ( $ssl_module eq 'Net::SSLeay' ) { no warnings 'uninitialized'; import Net::SSLeay qw(post_https make_headers make_form); my $headers = make_headers(%headers); $Net::SSLeay::trace = $opts->{'debug'} if exists $opts->{'debug'} && $opts->{'debug'}; my $raw_data = ref($post_data) ? make_form(%$post_data) : $post_data; $Net::SSLeay::trace = $opts->{'debug'} if exists $opts->{'debug'} && $opts->{'debug'}; my( $res_page, $res_code, @res_headers ) = post_https( $opts->{'host'}, $opts->{'port'}, $opts->{'path'}, $headers, $raw_data, $opts->{"Content-Type"}, ); $res_code =~ /^(HTTP\S+ )?(.*)/ and $res_code = $2; return ( $res_page, $res_code, @res_headers ); } elsif ( $ssl_module eq 'Crypt::SSLeay' ) { import HTTP::Request::Common qw(POST); my $url = 'https://' . $opts->{'host'}; $url .= ':' . $opts->{'port'} unless $opts->{'port'} == 443; $url .= $opts->{'path'}; my $ua = new LWP::UserAgent; foreach my $hdr ( keys %headers ) { $ua->default_header( $hdr => $headers{$hdr} ); } $ENV{HTTPS_DEBUG} = $opts->{'debug'} if exists $opts->{'debug'}; my $res; if ( ref($post_data) ) { $res = $ua->request( POST( $url, [%$post_data] ) ); } else { my $req = new HTTP::Request( 'POST' => $url ); $req->content_type( $opts->{"Content-Type"} ); $req->content($post_data); $res = $ua->request($req); } my @res_headers = map { $_ => $res->header($_) } $res->header_field_names; return ( $res->content, $res->code. ' '. $res->message, @res_headers ); } else { die "unknown SSL module $ssl_module"; } } =head1 AUTHOR Ivan Kohler, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc Net::HTTPS::Any You can also look for information at: =over 4 =item * RT: CPAN's request tracker L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 COPYRIGHT & LICENSE Copyright 2008-2010 Freeside Internet Services, Inc. (http://freeside.biz/) All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;