2652
2650
return ($scheme, $host, $port, $path_query);
2656
HTTPMicro::Handle; # hide from PAUSE/indexers
2661
use Errno qw[EINTR EPIPE];
2662
use IO::Socket qw[SOCK_STREAM];
2664
sub BUFSIZE () { 32768 }
2666
my $Printable = sub {
2671
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
2676
my ($class, %args) = @_;
2680
max_line_size => 16384,
2685
my $ssl_verify_args = {
2686
check_cn => "when_only",
2687
wildcards_in_alt => "anywhere",
2688
wildcards_in_cn => "anywhere"
2692
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
2693
my ($self, $scheme, $host, $port) = @_;
2695
if ( $scheme eq 'https' ) {
2696
eval "require IO::Socket::SSL"
2697
unless exists $INC{'IO/Socket/SSL.pm'};
2698
croak(qq/IO::Socket::SSL must be installed for https support\n/)
2699
unless $INC{'IO/Socket/SSL.pm'};
2701
elsif ( $scheme ne 'http' ) {
2702
croak(qq/Unsupported URL scheme '$scheme'\n/);
2705
$self->{fh} = 'IO::Socket::INET'->new(
2709
Type => SOCK_STREAM,
2710
Timeout => $self->{timeout}
2711
) or croak(qq/Could not connect to '$host:$port': $@/);
2713
binmode($self->{fh})
2714
or croak(qq/Could not binmode() socket: '$!'/);
2716
if ( $scheme eq 'https') {
2717
IO::Socket::SSL->start_SSL($self->{fh});
2718
ref($self->{fh}) eq 'IO::Socket::SSL'
2719
or die(qq/SSL connection failed for $host\n/);
2720
if ( $self->{fh}->can("verify_hostname") ) {
2721
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
2724
my $fh = $self->{fh};
2725
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
2726
or die(qq/SSL certificate not valid for $host\n/);
2730
$self->{host} = $host;
2731
$self->{port} = $port;
2737
@_ == 1 || croak(q/Usage: $handle->close()/);
2739
CORE::close($self->{fh})
2740
or croak(qq/Could not close socket: '$!'/);
2744
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
2745
my ($self, $buf) = @_;
2747
my $len = length $buf;
2750
local $SIG{PIPE} = 'IGNORE';
2754
or croak(q/Timed out while waiting for socket to become ready for writing/);
2755
my $r = syswrite($self->{fh}, $buf, $len, $off);
2759
last unless $len > 0;
2761
elsif ($! == EPIPE) {
2762
croak(qq/Socket closed by remote server: $!/);
2764
elsif ($! != EINTR) {
2765
croak(qq/Could not write to socket: '$!'/);
2772
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
2773
my ($self, $len) = @_;
2776
my $got = length $self->{rbuf};
2779
my $take = ($got < $len) ? $got : $len;
2780
$buf = substr($self->{rbuf}, 0, $take, '');
2786
or croak(q/Timed out while waiting for socket to become ready for reading/);
2787
my $r = sysread($self->{fh}, $buf, $len, length $buf);
2792
elsif ($! != EINTR) {
2793
croak(qq/Could not read from socket: '$!'/);
2797
croak(q/Unexpected end of stream/);
2803
@_ == 1 || croak(q/Usage: $handle->readline()/);
2807
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
2811
or croak(q/Timed out while waiting for socket to become ready for reading/);
2812
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
2816
elsif ($! != EINTR) {
2817
croak(qq/Could not read from socket: '$!'/);
2820
croak(q/Unexpected end of stream while looking for line/);
2823
sub read_header_lines {
2824
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
2825
my ($self, $headers) = @_;
2831
my $line = $self->readline;
2833
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
2834
my ($field_name) = lc $1;
2835
$val = \($headers->{$field_name} = $2);
2837
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
2839
or croak(q/Unexpected header continuation line/);
2840
next unless length $1;
2841
$$val .= ' ' if length $$val;
2844
elsif ($line =~ /\A \x0D?\x0A \z/x) {
2848
croak(q/Malformed header line: / . $Printable->($line));
2854
sub write_header_lines {
2855
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
2856
my($self, $headers) = @_;
2859
while (my ($k, $v) = each %$headers) {
2860
my $field_name = lc $k;
2861
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
2862
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
2863
$field_name =~ s/\b(\w)/\u$1/g;
2864
$buf .= "$field_name: $v\x0D\x0A";
2867
return $self->write($buf);
2870
sub read_content_body {
2871
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
2872
my ($self, $cb, $response, $len) = @_;
2873
$len ||= $response->{headers}{'content-length'};
2875
croak("No content-length in the returned response, and this "
2876
. "UA doesn't implement chunking") unless defined $len;
2879
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
2880
$cb->($self->read($read), $response);
2887
sub write_content_body {
2888
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
2889
my ($self, $request) = @_;
2890
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
2892
$len += $self->write($request->{content});
2894
$len == $content_length
2895
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
2900
sub read_response_header {
2901
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
2904
my $line = $self->readline;
2906
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
2907
or croak(q/Malformed Status-Line: / . $Printable->($line));
2909
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
2914
headers => $self->read_header_lines,
2915
protocol => $protocol,
2919
sub write_request_header {
2920
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
2921
my ($self, $method, $request_uri, $headers) = @_;
2923
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
2924
+ $self->write_header_lines($headers);
2928
my ($self, $type, $timeout) = @_;
2929
$timeout = $self->{timeout}
2930
unless defined $timeout && $timeout >= 0;
2932
my $fd = fileno $self->{fh};
2933
defined $fd && $fd >= 0
2934
or croak(q/select(2): 'Bad file descriptor'/);
2937
my $pending = $timeout;
2940
vec(my $fdset = '', $fd, 1) = 1;
2943
$nfound = ($type eq 'read')
2944
? select($fdset, undef, undef, $pending)
2945
: select(undef, $fdset, undef, $pending) ;
2946
if ($nfound == -1) {
2948
or croak(qq/select(2): '$!'/);
2949
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
2959
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
2961
return $self->_do_timeout('read', @_)
2965
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
2967
return $self->_do_timeout('write', @_)
2656
package HTTP::Micro::Handle;
2659
use warnings FATAL => 'all';
2660
use English qw(-no_match_vars);
2663
use Errno qw(EINTR EPIPE);
2664
use IO::Socket qw(SOCK_STREAM);
2666
sub BUFSIZE () { 32768 }
2668
my $Printable = sub {
2673
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
2678
my ($class, %args) = @_;
2682
max_line_size => 16384,
2687
my $ssl_verify_args = {
2688
check_cn => "when_only",
2689
wildcards_in_alt => "anywhere",
2690
wildcards_in_cn => "anywhere"
2694
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
2695
my ($self, $scheme, $host, $port) = @_;
2697
if ( $scheme eq 'https' ) {
2698
eval "require IO::Socket::SSL"
2699
unless exists $INC{'IO/Socket/SSL.pm'};
2700
croak(qq/IO::Socket::SSL must be installed for https support\n/)
2701
unless $INC{'IO/Socket/SSL.pm'};
2703
elsif ( $scheme ne 'http' ) {
2704
croak(qq/Unsupported URL scheme '$scheme'\n/);
2707
$self->{fh} = IO::Socket::INET->new(
2711
Type => SOCK_STREAM,
2712
Timeout => $self->{timeout}
2713
) or croak(qq/Could not connect to '$host:$port': $@/);
2715
binmode($self->{fh})
2716
or croak(qq/Could not binmode() socket: '$!'/);
2718
if ( $scheme eq 'https') {
2719
IO::Socket::SSL->start_SSL($self->{fh});
2720
ref($self->{fh}) eq 'IO::Socket::SSL'
2721
or die(qq/SSL connection failed for $host\n/);
2722
if ( $self->{fh}->can("verify_hostname") ) {
2723
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
2726
my $fh = $self->{fh};
2727
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
2728
or die(qq/SSL certificate not valid for $host\n/);
2732
$self->{host} = $host;
2733
$self->{port} = $port;
2739
@_ == 1 || croak(q/Usage: $handle->close()/);
2741
CORE::close($self->{fh})
2742
or croak(qq/Could not close socket: '$!'/);
2746
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
2747
my ($self, $buf) = @_;
2749
my $len = length $buf;
2752
local $SIG{PIPE} = 'IGNORE';
2756
or croak(q/Timed out while waiting for socket to become ready for writing/);
2757
my $r = syswrite($self->{fh}, $buf, $len, $off);
2761
last unless $len > 0;
2763
elsif ($! == EPIPE) {
2764
croak(qq/Socket closed by remote server: $!/);
2766
elsif ($! != EINTR) {
2767
croak(qq/Could not write to socket: '$!'/);
2774
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
2775
my ($self, $len) = @_;
2778
my $got = length $self->{rbuf};
2781
my $take = ($got < $len) ? $got : $len;
2782
$buf = substr($self->{rbuf}, 0, $take, '');
2788
or croak(q/Timed out while waiting for socket to become ready for reading/);
2789
my $r = sysread($self->{fh}, $buf, $len, length $buf);
2794
elsif ($! != EINTR) {
2795
croak(qq/Could not read from socket: '$!'/);
2799
croak(q/Unexpected end of stream/);
2805
@_ == 1 || croak(q/Usage: $handle->readline()/);
2809
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
2813
or croak(q/Timed out while waiting for socket to become ready for reading/);
2814
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
2818
elsif ($! != EINTR) {
2819
croak(qq/Could not read from socket: '$!'/);
2822
croak(q/Unexpected end of stream while looking for line/);
2825
sub read_header_lines {
2826
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
2827
my ($self, $headers) = @_;
2833
my $line = $self->readline;
2835
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
2836
my ($field_name) = lc $1;
2837
$val = \($headers->{$field_name} = $2);
2839
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
2841
or croak(q/Unexpected header continuation line/);
2842
next unless length $1;
2843
$$val .= ' ' if length $$val;
2846
elsif ($line =~ /\A \x0D?\x0A \z/x) {
2850
croak(q/Malformed header line: / . $Printable->($line));
2856
sub write_header_lines {
2857
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
2858
my($self, $headers) = @_;
2861
while (my ($k, $v) = each %$headers) {
2862
my $field_name = lc $k;
2863
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
2864
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
2865
$field_name =~ s/\b(\w)/\u$1/g;
2866
$buf .= "$field_name: $v\x0D\x0A";
2869
return $self->write($buf);
2872
sub read_content_body {
2873
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
2874
my ($self, $cb, $response, $len) = @_;
2875
$len ||= $response->{headers}{'content-length'};
2877
croak("No content-length in the returned response, and this "
2878
. "UA doesn't implement chunking") unless defined $len;
2881
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
2882
$cb->($self->read($read), $response);
2889
sub write_content_body {
2890
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
2891
my ($self, $request) = @_;
2892
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
2894
$len += $self->write($request->{content});
2896
$len == $content_length
2897
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
2902
sub read_response_header {
2903
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
2906
my $line = $self->readline;
2908
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
2909
or croak(q/Malformed Status-Line: / . $Printable->($line));
2911
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
2916
headers => $self->read_header_lines,
2917
protocol => $protocol,
2921
sub write_request_header {
2922
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
2923
my ($self, $method, $request_uri, $headers) = @_;
2925
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
2926
+ $self->write_header_lines($headers);
2930
my ($self, $type, $timeout) = @_;
2931
$timeout = $self->{timeout}
2932
unless defined $timeout && $timeout >= 0;
2934
my $fd = fileno $self->{fh};
2935
defined $fd && $fd >= 0
2936
or croak(q/select(2): 'Bad file descriptor'/);
2939
my $pending = $timeout;
2942
vec(my $fdset = '', $fd, 1) = 1;
2945
$nfound = ($type eq 'read')
2946
? select($fdset, undef, undef, $pending)
2947
: select(undef, $fdset, undef, $pending) ;
2948
if ($nfound == -1) {
2950
or croak(qq/select(2): '$!'/);
2951
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
2961
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
2963
return $self->_do_timeout('read', @_)
2967
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
2969
return $self->_do_timeout('write', @_)
2971
} # HTTP::Micro::Handle
2970
2973
my $prog = <<'EOP';