3760
3758
return ($scheme, $host, $port, $path_query);
3764
HTTPMicro::Handle; # hide from PAUSE/indexers
3769
use Errno qw[EINTR EPIPE];
3770
use IO::Socket qw[SOCK_STREAM];
3772
sub BUFSIZE () { 32768 }
3774
my $Printable = sub {
3779
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
3784
my ($class, %args) = @_;
3788
max_line_size => 16384,
3793
my $ssl_verify_args = {
3794
check_cn => "when_only",
3795
wildcards_in_alt => "anywhere",
3796
wildcards_in_cn => "anywhere"
3800
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
3801
my ($self, $scheme, $host, $port) = @_;
3803
if ( $scheme eq 'https' ) {
3804
eval "require IO::Socket::SSL"
3805
unless exists $INC{'IO/Socket/SSL.pm'};
3806
croak(qq/IO::Socket::SSL must be installed for https support\n/)
3807
unless $INC{'IO/Socket/SSL.pm'};
3809
elsif ( $scheme ne 'http' ) {
3810
croak(qq/Unsupported URL scheme '$scheme'\n/);
3813
$self->{fh} = 'IO::Socket::INET'->new(
3817
Type => SOCK_STREAM,
3818
Timeout => $self->{timeout}
3819
) or croak(qq/Could not connect to '$host:$port': $@/);
3821
binmode($self->{fh})
3822
or croak(qq/Could not binmode() socket: '$!'/);
3824
if ( $scheme eq 'https') {
3825
IO::Socket::SSL->start_SSL($self->{fh});
3826
ref($self->{fh}) eq 'IO::Socket::SSL'
3827
or die(qq/SSL connection failed for $host\n/);
3828
if ( $self->{fh}->can("verify_hostname") ) {
3829
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
3832
my $fh = $self->{fh};
3833
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
3834
or die(qq/SSL certificate not valid for $host\n/);
3838
$self->{host} = $host;
3839
$self->{port} = $port;
3845
@_ == 1 || croak(q/Usage: $handle->close()/);
3847
CORE::close($self->{fh})
3848
or croak(qq/Could not close socket: '$!'/);
3852
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
3853
my ($self, $buf) = @_;
3855
my $len = length $buf;
3858
local $SIG{PIPE} = 'IGNORE';
3862
or croak(q/Timed out while waiting for socket to become ready for writing/);
3863
my $r = syswrite($self->{fh}, $buf, $len, $off);
3867
last unless $len > 0;
3869
elsif ($! == EPIPE) {
3870
croak(qq/Socket closed by remote server: $!/);
3872
elsif ($! != EINTR) {
3873
croak(qq/Could not write to socket: '$!'/);
3880
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
3881
my ($self, $len) = @_;
3884
my $got = length $self->{rbuf};
3887
my $take = ($got < $len) ? $got : $len;
3888
$buf = substr($self->{rbuf}, 0, $take, '');
3894
or croak(q/Timed out while waiting for socket to become ready for reading/);
3895
my $r = sysread($self->{fh}, $buf, $len, length $buf);
3900
elsif ($! != EINTR) {
3901
croak(qq/Could not read from socket: '$!'/);
3905
croak(q/Unexpected end of stream/);
3911
@_ == 1 || croak(q/Usage: $handle->readline()/);
3915
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
3919
or croak(q/Timed out while waiting for socket to become ready for reading/);
3920
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
3924
elsif ($! != EINTR) {
3925
croak(qq/Could not read from socket: '$!'/);
3928
croak(q/Unexpected end of stream while looking for line/);
3931
sub read_header_lines {
3932
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
3933
my ($self, $headers) = @_;
3939
my $line = $self->readline;
3941
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
3942
my ($field_name) = lc $1;
3943
$val = \($headers->{$field_name} = $2);
3945
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
3947
or croak(q/Unexpected header continuation line/);
3948
next unless length $1;
3949
$$val .= ' ' if length $$val;
3952
elsif ($line =~ /\A \x0D?\x0A \z/x) {
3956
croak(q/Malformed header line: / . $Printable->($line));
3962
sub write_header_lines {
3963
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
3964
my($self, $headers) = @_;
3967
while (my ($k, $v) = each %$headers) {
3968
my $field_name = lc $k;
3969
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
3970
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
3971
$field_name =~ s/\b(\w)/\u$1/g;
3972
$buf .= "$field_name: $v\x0D\x0A";
3975
return $self->write($buf);
3978
sub read_content_body {
3979
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
3980
my ($self, $cb, $response, $len) = @_;
3981
$len ||= $response->{headers}{'content-length'};
3983
croak("No content-length in the returned response, and this "
3984
. "UA doesn't implement chunking") unless defined $len;
3987
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
3988
$cb->($self->read($read), $response);
3995
sub write_content_body {
3996
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
3997
my ($self, $request) = @_;
3998
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
4000
$len += $self->write($request->{content});
4002
$len == $content_length
4003
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
4008
sub read_response_header {
4009
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
4012
my $line = $self->readline;
4014
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
4015
or croak(q/Malformed Status-Line: / . $Printable->($line));
4017
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
4022
headers => $self->read_header_lines,
4023
protocol => $protocol,
4027
sub write_request_header {
4028
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
4029
my ($self, $method, $request_uri, $headers) = @_;
4031
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
4032
+ $self->write_header_lines($headers);
4036
my ($self, $type, $timeout) = @_;
4037
$timeout = $self->{timeout}
4038
unless defined $timeout && $timeout >= 0;
4040
my $fd = fileno $self->{fh};
4041
defined $fd && $fd >= 0
4042
or croak(q/select(2): 'Bad file descriptor'/);
4045
my $pending = $timeout;
4048
vec(my $fdset = '', $fd, 1) = 1;
4051
$nfound = ($type eq 'read')
4052
? select($fdset, undef, undef, $pending)
4053
: select(undef, $fdset, undef, $pending) ;
4054
if ($nfound == -1) {
4056
or croak(qq/select(2): '$!'/);
4057
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
4067
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
4069
return $self->_do_timeout('read', @_)
4073
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
4075
return $self->_do_timeout('write', @_)
3764
package HTTP::Micro::Handle;
3767
use warnings FATAL => 'all';
3768
use English qw(-no_match_vars);
3771
use Errno qw(EINTR EPIPE);
3772
use IO::Socket qw(SOCK_STREAM);
3774
sub BUFSIZE () { 32768 }
3776
my $Printable = sub {
3781
s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
3786
my ($class, %args) = @_;
3790
max_line_size => 16384,
3795
my $ssl_verify_args = {
3796
check_cn => "when_only",
3797
wildcards_in_alt => "anywhere",
3798
wildcards_in_cn => "anywhere"
3802
@_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
3803
my ($self, $scheme, $host, $port) = @_;
3805
if ( $scheme eq 'https' ) {
3806
eval "require IO::Socket::SSL"
3807
unless exists $INC{'IO/Socket/SSL.pm'};
3808
croak(qq/IO::Socket::SSL must be installed for https support\n/)
3809
unless $INC{'IO/Socket/SSL.pm'};
3811
elsif ( $scheme ne 'http' ) {
3812
croak(qq/Unsupported URL scheme '$scheme'\n/);
3815
$self->{fh} = IO::Socket::INET->new(
3819
Type => SOCK_STREAM,
3820
Timeout => $self->{timeout}
3821
) or croak(qq/Could not connect to '$host:$port': $@/);
3823
binmode($self->{fh})
3824
or croak(qq/Could not binmode() socket: '$!'/);
3826
if ( $scheme eq 'https') {
3827
IO::Socket::SSL->start_SSL($self->{fh});
3828
ref($self->{fh}) eq 'IO::Socket::SSL'
3829
or die(qq/SSL connection failed for $host\n/);
3830
if ( $self->{fh}->can("verify_hostname") ) {
3831
$self->{fh}->verify_hostname( $host, $ssl_verify_args );
3834
my $fh = $self->{fh};
3835
_verify_hostname_of_cert($host, _peer_certificate($fh), $ssl_verify_args)
3836
or die(qq/SSL certificate not valid for $host\n/);
3840
$self->{host} = $host;
3841
$self->{port} = $port;
3847
@_ == 1 || croak(q/Usage: $handle->close()/);
3849
CORE::close($self->{fh})
3850
or croak(qq/Could not close socket: '$!'/);
3854
@_ == 2 || croak(q/Usage: $handle->write(buf)/);
3855
my ($self, $buf) = @_;
3857
my $len = length $buf;
3860
local $SIG{PIPE} = 'IGNORE';
3864
or croak(q/Timed out while waiting for socket to become ready for writing/);
3865
my $r = syswrite($self->{fh}, $buf, $len, $off);
3869
last unless $len > 0;
3871
elsif ($! == EPIPE) {
3872
croak(qq/Socket closed by remote server: $!/);
3874
elsif ($! != EINTR) {
3875
croak(qq/Could not write to socket: '$!'/);
3882
@_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len)/);
3883
my ($self, $len) = @_;
3886
my $got = length $self->{rbuf};
3889
my $take = ($got < $len) ? $got : $len;
3890
$buf = substr($self->{rbuf}, 0, $take, '');
3896
or croak(q/Timed out while waiting for socket to become ready for reading/);
3897
my $r = sysread($self->{fh}, $buf, $len, length $buf);
3902
elsif ($! != EINTR) {
3903
croak(qq/Could not read from socket: '$!'/);
3907
croak(q/Unexpected end of stream/);
3913
@_ == 1 || croak(q/Usage: $handle->readline()/);
3917
if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
3921
or croak(q/Timed out while waiting for socket to become ready for reading/);
3922
my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
3926
elsif ($! != EINTR) {
3927
croak(qq/Could not read from socket: '$!'/);
3930
croak(q/Unexpected end of stream while looking for line/);
3933
sub read_header_lines {
3934
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
3935
my ($self, $headers) = @_;
3941
my $line = $self->readline;
3943
if ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
3944
my ($field_name) = lc $1;
3945
$val = \($headers->{$field_name} = $2);
3947
elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
3949
or croak(q/Unexpected header continuation line/);
3950
next unless length $1;
3951
$$val .= ' ' if length $$val;
3954
elsif ($line =~ /\A \x0D?\x0A \z/x) {
3958
croak(q/Malformed header line: / . $Printable->($line));
3964
sub write_header_lines {
3965
(@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
3966
my($self, $headers) = @_;
3969
while (my ($k, $v) = each %$headers) {
3970
my $field_name = lc $k;
3971
$field_name =~ /\A [\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]+ \z/x
3972
or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
3973
$field_name =~ s/\b(\w)/\u$1/g;
3974
$buf .= "$field_name: $v\x0D\x0A";
3977
return $self->write($buf);
3980
sub read_content_body {
3981
@_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
3982
my ($self, $cb, $response, $len) = @_;
3983
$len ||= $response->{headers}{'content-length'};
3985
croak("No content-length in the returned response, and this "
3986
. "UA doesn't implement chunking") unless defined $len;
3989
my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
3990
$cb->($self->read($read), $response);
3997
sub write_content_body {
3998
@_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
3999
my ($self, $request) = @_;
4000
my ($len, $content_length) = (0, $request->{headers}{'content-length'});
4002
$len += $self->write($request->{content});
4004
$len == $content_length
4005
or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
4010
sub read_response_header {
4011
@_ == 1 || croak(q/Usage: $handle->read_response_header()/);
4014
my $line = $self->readline;
4016
$line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
4017
or croak(q/Malformed Status-Line: / . $Printable->($line));
4019
my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
4024
headers => $self->read_header_lines,
4025
protocol => $protocol,
4029
sub write_request_header {
4030
@_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
4031
my ($self, $method, $request_uri, $headers) = @_;
4033
return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
4034
+ $self->write_header_lines($headers);
4038
my ($self, $type, $timeout) = @_;
4039
$timeout = $self->{timeout}
4040
unless defined $timeout && $timeout >= 0;
4042
my $fd = fileno $self->{fh};
4043
defined $fd && $fd >= 0
4044
or croak(q/select(2): 'Bad file descriptor'/);
4047
my $pending = $timeout;
4050
vec(my $fdset = '', $fd, 1) = 1;
4053
$nfound = ($type eq 'read')
4054
? select($fdset, undef, undef, $pending)
4055
: select(undef, $fdset, undef, $pending) ;
4056
if ($nfound == -1) {
4058
or croak(qq/select(2): '$!'/);
4059
redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
4069
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
4071
return $self->_do_timeout('read', @_)
4075
@_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
4077
return $self->_do_timeout('write', @_)
4079
} # HTTP::Micro::Handle
4078
4081
my $prog = <<'EOP';